一:MobileProject简介
MobileProject 项目是一个以 MVC 模式搭建的开源功能集合,基于 Objective-C 上面进行编写,意在解决新项目对于常见功能模块的重复开发, MobileProject 对于项目的搭建也进行很明确的划分,各个模块职责也比较明确, MobileProject 也引入的一些常用第三方插件、宏定义、工具帮助类等;整个项目也是在不断更新跟维护中,功能点也会不断更新;代码支持 iOS7 以后版本;
二:项目框架内容
相信关于JSPatch插件用于项目的热更新应该是比较常见的功能,在 MobileProject 里面就实现一个关于热更新的效果,并且有封装一个帮助类,对于热更新的 JS 文件下载及运用进行说明;包含一些下载的次数控制等;
主要源代码如下:
+(void)HSDevaluateScript
{
//从本地获取下载的JS文件
NSURL *p = FilePath;
//判断文件是否存在
NSString *curFilePath=[p.path stringByAppendingString:[NSString stringWithFormat:@"/%@",jsPatchJsFileName]];
if (![[NSFileManager defaultManager] fileExistsAtPath:curFilePath]) {
return;
}
//获取内容
NSString *js = [NSString stringWithContentsOfFile:curFilePath encoding:NSUTF8StringEncoding error:nil];
//如果有内容
if (js.length > 0)
{
//-------
//服务端要对JS内容进行加密,在此处解密js内容;增加安全性
//----
//运行
[JPEngine startEngine];
[JPEngine evaluateScript:js];
}
}
+(void)loadJSPatch
{
//优化间隔一段时间 再去请求一次 否则太频繁(这边定义为一个小时才去请求一次)
NSDate *myNowDate=[NSDate date];
if (!BBUserDefault.MBJsPatchTime) {
BBUserDefault.MBJsPatchTime=myNowDate;
}
if ([myNowDate timeIntervalSinceDate:BBUserDefault.MBJsPatchTime]<3600) {
return;
}
//重新赋值
BBUserDefault.MBJsPatchTime=myNowDate;
//使用AFNetWork下载在服务器的js文件
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:kJSPatchServerPath];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if (httpResponse.statusCode==200) {
NSURL *documentsDirectoryURL = FilePath;
//保存到本地 Library/Caches目录下
return [documentsDirectoryURL URLByAppendingPathComponent:jsPatchJsFileName];
}
else
{
return nil;
}
}
completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
{
NSLog(@"下载失败 to: %@", filePath);
}];
[downloadTask resume];
}
关于详细的运用可以下载源代码进行查看;
在平时开发过程中经常会碰到跟数据库打交道,而 LKDB 是一个不错的实体映射成数据库插件,可以很轻松就能完成实体针数据库列的映射,并能进行一些在实体层面上的增删改查的操作,当然也可以进行 SQL 语句的运用;能够满足我们平时项目的运用;
主要源代码如下:
+(LKDBHelper *)getUsingLKDBHelper
{
static LKDBHelper* db;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *sqlitePath = [MPLKDBHelper downloadPath];
NSString* dbpath = [sqlitePath stringByAppendingPathComponent:[NSString stringWithFormat:@"MPData.db"]];
NSLog(@"当前创建数据库地址路径:%@",dbpath);
db = [[LKDBHelper alloc]initWithDBPath:dbpath];
});
return db;
}
/**
* @author wujunyang, 15-05-21 16:05:44
*
* @brief 路径
* @return
*/
+ (NSString *)downloadPath{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *downloadPath = [documentPath stringByAppendingPathComponent:@"DataBase"];
NSLog(@"%@",downloadPath);
return downloadPath;
}
上面只是简单列出关于数据库 SQList 的创建,关于运用可以查看源代码;
在平常项目中对于地图的运用可能包含显示跟定位等相关的内容, MobileProject 项目里有一个关于百度地图的车行路线生成实例,并且修改大头针跟弹出窗的效果,代码中还运用百度地图进行定位,并对手机不同的语言进行定位城市的处理功能;使其在定位获取城市名字时一定是中文,排除由于手机设置语言的原因导致城市名称不对等;
主要源代码如下:
//百度地图定位
[[MPLocationManager shareInstance] startBMKLocationWithReg:^(BMKUserLocation *loction, NSError *error) {
if (error) {
DDLogError(@"定位失败,失败原因:%@",error);
}
else
{
DDLogError(@"定位信息:%f,%f",loction.location.coordinate.latitude,loction.location.coordinate.longitude);
CLGeocoder *geocoder=[[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:loction.location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
//处理手机语言 获得城市的名称(中文)
NSMutableArray *userDefaultLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSString *currentLanguage = [userDefaultLanguages objectAtIndex:0];
//如果不是中文 则强制先转成中文 获得后再转成默认语言
if (![currentLanguage isEqualToString:@"zh-Hans"]&&![currentLanguage isEqualToString:@"zh-Hans-CN"]) {
//IOS9前后区分
if (isIOS9) {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans-CN", nil] forKey:@"AppleLanguages"];
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", nil] forKey:@"AppleLanguages"];
}
}
//转换地理信息
if (placemarks.count>0) {
CLPlacemark *placemark=[placemarks objectAtIndex:0];
//获取城市
NSString *city = placemark.locality;
if (!city) {
//四大直辖市的城市信息无法通过locality获得,只能通过获取省份的方法来获得(如果city为空,则可知为直辖市)
city = placemark.administrativeArea;
}
NSLog(@"百度当前城市:[%@]",city);
// 城市名传出去后,立即 Device 语言 还原为默认的语言
[[NSUserDefaults standardUserDefaults] setObject:userDefaultLanguages forKey:@"AppleLanguages"];
}
}];
}
}];
关于地图的展现请查看源代码的内容;上面的代码是主要运用于定位功能
MobileProject 中的二维码主要是在基于插件LBXScan上进行开发,其封装已经满足平常我们见到的二维效果,项目实例主要是包含关于扫描跟从相册选取照片然后读取上面的二维码效果,当然LBXScan还可以读取条形码的功能,可以去查看其官方实例效果;
主要源代码如下:
//设置样式
- (LBXScanViewStyle *)qqLBXScanViewStyle
{
//设置扫码区域参数设置
//创建参数对象
LBXScanViewStyle *style = [[LBXScanViewStyle alloc]init];
//矩形区域中心上移,默认中心点为屏幕中心点
style.centerUpOffset = 44;
//扫码框周围4个角的类型,设置为外挂式
style.photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle_Outer;
//扫码框周围4个角绘制的线条宽度
style.photoframeLineW = 6;
//扫码框周围4个角的宽度
style.photoframeAngleW = 24;
//扫码框周围4个角的高度
style.photoframeAngleH = 24;
//扫码框内 动画类型 --线条上下移动
style.anmiationStyle = LBXScanViewAnimationStyle_LineMove;
//线条上下移动图片
style.animationImage = [UIImage imageNamed:@"qrcode_scan_light_green"];
return style;
}
//获得扫码的结果
- (void)scanResultWithArray:(NSArray*)array
{
if (array.count < 1)
{
[self popAlertMsgWithScanResult:nil];
return;
}
//经测试,可以同时识别2个二维码,不能同时识别二维码和条形码
for (LBXScanResult *result in array) {
NSLog(@"scanResult:%@",result.strScanned);
}
LBXScanResult *scanResult = array[0];
NSString*strResult = scanResult.strScanned;
self.scanImage = scanResult.imgScanned;
if (!strResult) {
[self popAlertMsgWithScanResult:nil];
return;
}
//震动提醒
//[LBXScanWrapper systemVibrate];
//声音提醒
//[LBXScanWrapper systemSound];
[MBProgressHUD showSuccess:[NSString stringWithFormat:@"扫码的内容为:%@",strResult] ToView:nil];
}
照片上传应该是每个 APP 必备的功能模块,所以 MobileProject 对它进行的一个简单整理,主要实现了,包含选择照片、拍照、浏览大图、获得图片 GPS 、图片名称、图片拍照时间、上传时对图片进行转正调整、压缩图片、图片展现效果等,项目中也还有另外一种上传效果,就是带进度的上传,选择完几张照片它会每张进间上传并有相应的扇形进度效果;
主要源代码如下:
+ (instancetype)imageWithAssetURL:(NSURL *)assetURL isUploadProcess:(BOOL)isUploadProcess{
MPImageItemModel *imageItem = [[MPImageItemModel alloc] init];
imageItem.uploadState = MPImageUploadStateInit;
imageItem.assetURL = assetURL;
MPWeakSelf(self);
void (^selectAsset)(ALAsset *) = ^(ALAsset *asset){
if (asset) {
UIImage *highQualityImage = [imageCompressHelper fullScreenImageALAsset:asset];
UIImage *thumbnailImage = [UIImage imageWithCGImage:[asset thumbnail]];
//照片信息
MPStrongSelf(self);
[self imageInfoWithALAsset:asset imageItem:imageItem];
dispatch_async(dispatch_get_main_queue(), ^{
imageItem.image = [imageCompressHelper compressedImageToLimitSizeOfKB:100 image:highQualityImage];;
imageItem.thumbnailImage = thumbnailImage;
if (isUploadProcess) {
//上传到沙盒 成功上传后记得删除对应
[MPFileManager writeUploadDataWithName:imageItem.photoName andImage:imageItem.image];
}
});
}
};
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
MPWeakSelf(assetsLibrary);
[assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {
if (asset) {
selectAsset(asset);
}else{
MPStrongSelf(assetsLibrary);
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupPhotoStream usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stopG) {
if([result.defaultRepresentation.url isEqual:assetURL]) {
selectAsset(result);
*stop = YES;
*stopG = YES;
}
}];
} failureBlock:^(NSError *error) {
NSLog(@"读取图片失败");
}];
}
}failureBlock:^(NSError *error) {
NSLog(@"读取图片失败");
}];
return imageItem;
}
针对目前 iPhone 机型已经越来越多的状态下,在设计只出一种效果图的情况下,要在不同的屏幕大小显示出不同的字体大小跟布局,在 MobileProject 定义的几种宏,其假设效果图是用 iphone5 出,通过这几个宏的运用就可以兼容在 iPhone6+ 等下的布局,解决以前关于大屏字体变小等问题;
主要源代码如下:
//不同屏幕尺寸字体适配(320,568是因为效果图为IPHONE5 如果不是则根据实际情况修改)
#define kScreenWidthRatio (Main_Screen_Width / 320.0)
#define kScreenHeightRatio (Main_Screen_Height / 568.0)
#define AdaptedWidth(x) ceilf((x) * kScreenWidthRatio)
#define AdaptedHeight(x) ceilf((x) * kScreenHeightRatio)
#define AdaptedFontSize(R) CHINESE_SYSTEM(AdaptedWidth(R))
-(void)LayoutLayoutCell
{
if (!self.myDateLabel) {
self.myDateLabel=[[UILabel alloc]init];
self.myDateLabel.font=AdaptedFontSize(13);
self.myDateLabel.textColor=[UIColor blackColor];
[self.contentView addSubview:self.myDateLabel];
[self.myDateLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(AdaptedHeight(KTopSpace));
make.left.mas_equalTo(KLeftSpace);
make.right.mas_equalTo(-KLeftSpace);
make.height.mas_equalTo(AdaptedHeight(15));
}];
}
if (!self.myTextLabel) {
self.myTextLabel=[[UILabel alloc]init];
self.myTextLabel.font=AdaptedFontSize(KTextLabelFontSize);
self.myTextLabel.textColor=[UIColor grayColor];
self.myTextLabel.numberOfLines=0;
[self.myTextLabel sizeToFit];
[self.contentView addSubview:self.myTextLabel];
[self.myTextLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.myDateLabel.bottom).offset(AdaptedHeight(KTopSpace));
make.left.mas_equalTo(KLeftSpace);
make.right.mas_equalTo(-KLeftSpace);
}];
}
}
-(void)configCellWithText:(NSString *)text dateText:(NSString *)dateText
{
self.myDateLabel.text=dateText;
self.myTextLabel.attributedText = [MPAdaptationCell cellTextAttributed:text];
}
+(CGFloat)cellHegith:(NSString *)text
{
CGFloat result=3*AdaptedHeight(10)+AdaptedHeight(15);
if (text.length>0) {
result=result+[[self cellTextAttributed:text] boundingRectWithSize:CGSizeMake(Main_Screen_Width-2*KLeftSpace, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size.height;
}
return result;
}
如果你们项目的效果图是以其它机型为准,则要修改宏定义的那个比例值;
日志记录功能应该是每个 APP 必备,项目中运用了 CocoaLumberjack 进行日志记录的展示,并在公共页面里已经增加了两个展现页,用于展现记录的内容,在详细的错误记录里面还有一个邮件发送的功能,可以把错误内容通过邮件发送给开发人员,解决项目上线后关于BUG的收集难题, MobileProject 中也根据不同的环境设置记录等级,也在项目里面增加在控制台进行有色字体提示;
主要源代码如下:
- (void)configureLogging
{
// Enable XcodeColors利用XcodeColors显示色彩(不写没效果)
setenv("XcodeColors", "YES", 0);
[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
[DDLog addLogger:self.fileLogger];
//设置颜色值
[[DDTTYLogger sharedInstance] setColorsEnabled:YES];
[[DDTTYLogger sharedInstance] setForegroundColor:[UIColor blueColor] backgroundColor:nil forFlag:DDLogFlagInfo];
[[DDTTYLogger sharedInstance] setForegroundColor:[UIColor purpleColor] backgroundColor:nil forFlag:DDLogFlagDebug];
[[DDTTYLogger sharedInstance] setForegroundColor:[UIColor redColor] backgroundColor:nil forFlag:DDLogFlagError];
[[DDTTYLogger sharedInstance] setForegroundColor:[UIColor greenColor] backgroundColor:nil forFlag:DDLogFlagVerbose];
//设置输出的LOG样式
MPLoggerFormatter* formatter = [[MPLoggerFormatter alloc] init];
[[DDTTYLogger sharedInstance] setLogFormatter:formatter];
}
#pragma mark - Getters
- (DDFileLogger *)fileLogger
{
if (!_fileLogger) {
DDFileLogger *fileLogger = [[DDFileLogger alloc] init];
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
_fileLogger = fileLogger;
}
return _fileLogger;
}
关于其它日志的内容可以查看源代码;包含关于日志的展现跟邮件发送的源代码;
项目中有时在列表没有数据或者说是网络请求出现的情况下是要有相关提示, MobileProject 也引入的一个空白提示效果用于其功能,基本上可以满足平时的开发要求;
主要源代码如下:
-(void)loadMyTableData
{
//请求服务端接口并返回数据
__weak typeof(self)weakSelf = self;
//成功时
[self.myTableView reloadData];
[self.myTableView.mj_header endRefreshing];
//增加无数据展现
[self.view configBlankPage:EaseBlankPageTypeView hasData:self.dataArray.count hasError:(self.dataArray.count>0) reloadButtonBlock:^(id sender) {
[MBProgressHUD showInfo:@"重新加载的数据" ToView:self.view];
[weakSelf.myTableView.mj_header beginRefreshing];
}];
//失败时
// [self.view configBlankPage:EaseBlankPageTypeView hasData:(self.dataArray.count>0) hasError:YES reloadButtonBlock:^(id sender) {
// [MBProgressHUD showInfo:@"重新加载的数据" ToView:self.view];
// [weakSelf.myTableView reloadData];
// }];
}
关于空白页的编写可以直接看源代码了,是一个 UIView 的分类,也可以根据项目对它进行调整满要求;
自定义的弹出窗 UIAlert 在 IOS7 跟 IOS8 以后是有不一样的效果,经常会在一些项目中要求模拟系统的 UIAlert 的样式进行编写,针对这种情况加入的一个自定义弹出窗,模拟系统 UIAlertView 效果 , 增加一个带 UITextView 的弹出效果,其它自定义视图根据项目再创建;实例中有几个这方面的实例;
主要源代码如下:
WJYAlertInputTextView *myInputView=[[WJYAlertInputTextView alloc]initPagesViewWithTitle:@"消息内容" leftButtonTitle:@"取消" rightButtonTitle:@"确定" placeholderText:@"请输入正确的订单号"];
__weak typeof(self)weakSelf = self;
myInputView.leftBlock=^(NSString *text)
{
NSLog(@"当前值:%@",text);
[weakSelf.alertView dismissWithCompletion:nil];
};
myInputView.rightBlock=^(NSString *text)
{
if (text.length==0) {
//ToView:weakSelf.alertView这样才会显示出来 否则会被AlertView盖住
[MBProgressHUD showError:@"内容没有输入" ToView:weakSelf.alertView];
return;
}
weakSelf.alertView.window.windowLevel = UIWindowLevelStatusBar +1;
[MBProgressHUD showAutoMessage:[NSString stringWithFormat:@"当前内容为:%@",text]];
[weakSelf.alertView dismissWithCompletion:nil];
};
_alertView=[[WJYAlertView alloc]initWithCustomView:myInputView dismissWhenTouchedBackground:NO];
[_alertView show];
其它效果可以直接查看源代码,也可以照了源代码给出的实例创建出各种种样的自定义弹出窗效果;
a :关于引导页功能的封装,只要简单传入一组图片就可以实现引导页的功能模块
b :关于启动广告功能的封装,同样也是传入一组图片就可以有展现效果,图片还是加载服务端,下载并保存在本地;
c :友盟管理帮助类的封装,主要是一些关于友盟统计的代码跟页面记录功能,结合 runtime 功能进行记录效果
d :记录设备唯一标识功能,在 IOS 中现在是不能再获取设备的唯一码,在 MobileProject 引入的一个插件从而可以获取设备的 FCUUID ,同样可以达到相应的效果;
e :省市区三级联动的效果功能,从本地读取省市区数据并加载,可以绑定默认值及选择后的效果;
f :还有关于友盟第三方登录的功能及友盟第三方分享的功能
g :封装 MBProgressHUD 扩展类,定义一些常见的提示效果,详见 MBProgressHUD+MP 类
h:集成 CYLTabBarController 插件,为项目增加底部 4 个 TabBar 菜单,并且有相应的未读提醒效果,及点击事件的运用;
i:集成个推消息推送功能( ThirdMacros.h 修改相应的 key 值),证书也要用你们自个的消息证书;
j:增加 FLEX ,在本地测试版本开启, FLEX 是 Flipboard 官方发布的一组专门用于 iOS 开发的应用内调试工具,能在模拟器和物理设备上良好运作,而开发者也无需将其连接到 LLDB/Xcode 或其他远程调试服务器,即可直接查看或修改正在运行的 App 的每一处状态。
k: UITableViewCell 倒计时功能 , 实例因为没有服务端接口,所以时间都以本地时间为准,正式项目时间都要从服务端获取;
l:引入 WebViewJavascriptBridge 进行 H5 交互,并对官网实例进行注解
四:总结
上面主要列出一些目前项目中的封装或者是实例功能模块,还有一些其它的运用就没有在这详细进行讲解,比如网络运用、宏定义、分类扩展类、其它小型帮助类及常用的第三方插件等,可以下载源代码进行查看,项目也在不断的完善中,对于项目的构架也在提一步的提升,对于编写的代码也不断的优化;
项目的源代码地址: https://github.com/wujunyang/MobileProject
如果喜欢或者有帮助可以点星哈,如果您也有空闲时间可以一起完善,保持关注会不断的更新功能;