前不久和朋友一起合作了一个外包App项目,我负责iOS和后台Server,另外两名同学合作开发Android端。现在项目已经进入收尾上架工作,我将就开发过程中的一些感想与收获进行总结。
使用CocoaPods做依赖管理以及开发用到的第三方开源项目
CocoaPods真是依赖管理神器,比Andorid的Maven或者Gradle方便多了。具体试用可以看看这篇文章唐巧-用CocoaPods做iOS程序的依赖管理
而且最近CocoaPods的Spec有了第三方国内镜像,速度快多了,为什么之前开发的时候木有,加一个库要等半天(ノДT) 。 所以我之前修改Podfile后是这样更新的(不更新Spec库并且有提示):
1
| pod install --verbose --no-repo-update
|
我项目里面的第三方依赖有:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#import <AVOSCloud/AVOSCloud.h> // AVOSCloud SDK
#import <MAMapKit/MAMapKit.h> // 高德地图
#import <AFNetworking/AFNetworking.h> // AFNetworking 网络通信
#import <UIColor+ChineseColor.h> // 自己的项目复用模块
#import <MMCommon/MMCommon.h>
#import <SVProgressHUD/SVProgressHUD.h> // 加载提示的HUD
#import <Underscore.m/Underscore.h> // Objective-C的类似JavaScript的Underscore.js的辅助库
#import <UMengAnalytics/MobClick.h> // 友盟统计和SNS集成
#import <TencentOpenAPI/TencentApiInterface.h>
#import <TencentOpenAPI/QQApiInterface.h>
#import <TencentOpenAPI/TencentOAuth.h>
#import <UMengSocial/UMSocialData.h>
#import <UMengSocial/UMSocialSnsService.h>
#import <SDWebImage/UIButton+WebCache.h> // 异步图片加载
#import <SDWebImage/UIImage+GIF.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import <SDWebImage/UIImageView+WebCache.h>
|
不得不提的是使用中。。友盟以及SNS集成过程中,不够清楚的文档以及那些坑的地方,还有调试了几天才发现是SDK悄悄升级,之前的SDK不能用的情况。
在使用的时候,为了更优雅的进行依赖管理,我也为高德地图和友盟SNS创建了Podfile并且提交到了CocoaPods/Spec库里面,需要的童鞋可以看看https://github.com/oa414/Specs/commits/master
我们同时用了AVOSCloud与友盟,这两个第三方开发者平台功能有很多重复,但是当前各有长处。未来可能迁移到其中一个。
iOS MVC与界面布局的一些实践
最初的时候,为了快速开发,全部使用了Storyboard,这让我在一个小时就把几十个页面跳转关系全部做好了。。。而且可以作为原型演示给UI童鞋和Android童鞋看。到后来,发现Storyboard管理几十个页面完全是坑,很多东西代码写更方便,并且即使是23寸的显示器在Interface Building里面拖动各个View也好麻烦。。。于是把这个Tab布局的应用按照五个Tab拆成五个StoryBoard了。主要用代码做布局和跳转,同时取巧用了StoryBoard一些方便的地方。
把各个Tab对应内容分离到不同的StoryBoard的方法:(代码好丑)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
+(UIViewController *)initViewController :(NSString *)storyBoardName
identifier:(NSString *)identifier
iconName:(NSString *)iconName
withTag:(int) tag{
UIViewController *vc = [SXAppDelegate viewControllerWithStoryBoard:storyBoardName identifier:identifier];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[nav.tabBarItem setImage:[SXAppDelegate getIconImage:iconName]];
nav.tabBarItem.image = [[SXAppDelegate getIconImage:iconName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *selectImage = [SXAppDelegate getIconPressedImage:iconName];
selectImage = [selectImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
DebugLog(@"select Image %@", selectImage);
[nav.tabBarItem setSelectedImage:selectImage];
nav.tabBarItem.selectedImage = selectImage;
[[UITabBar appearance] setTintColor:[SXAppDelegate getNavColor:iconName]];
nav.tabBarItem.tag = tag;
[nav.tabBarItem setTitleTextAttributes:@{
NSForegroundColorAttributeName : [SXAppDelegate getNavColor:iconName] } forState:UIControlStateSelected];
NSDictionary *titles = @{
@"home" : @"首页",
@"product":@"产品",
@"news": @"新闻",
@"my": @"我的",
@"more": @"更多",
};
[nav.tabBarItem setTitle:titles[iconName]];
return nav;
}
self.tabBarController = [[UITabBarController alloc] init];
self.regVC = [SXAppDelegate initViewController:@"My" identifier:@"login" iconName:@"my" withTag:3];
self.userVC = [SXAppDelegate initViewController:@"My" identifier:@"index" iconName:@"my" withTag:3];
self.indexVC = [SXAppDelegate initViewController:@"Main" identifier:@"index" iconName:@"home" withTag:0];
self.productVC = [SXAppDelegate initViewController:@"Product" identifier:@"index" iconName:@"product" withTag:1];
self.newsVC = [SXAppDelegate initViewController:@"News" identifier:@"index" iconName:@"news" withTag:2];
self.myVC = self.regVC;
self.moreVC = [SXAppDelegate initViewController:@"More" identifier:@"index" iconName:@"more" withTag:4];
DebugLog(@"Splash %@", [SXSplashHelper splashImage]);
if ([SXUser currentUser] == nil){
[self setRegView];
}else {
[self setUserView];
}
self.window.rootViewController = self.tabBarController;
|
宏
有几个常用的宏可以和大家分享下:
Debug环境下输出Log与行号
1
2
3
4
| #ifdef DEBUG
#define DebugLog( s, ... ) NSLog( @"[FILE]%@ %*s [LINE]%-*d [METHOD]%@ %*s [MESSAGE]%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent],30 - ([[[NSString stringWithUTF8String:__FILE__] lastPathComponent] length]),"", 5,__LINE__, NSStringFromSelector(_cmd), 75 - ([NSStringFromSelector(_cmd) length]),"", [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )
|
用RGB生成UIColor
1
2
3
4
| #define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
|
获取App版本号
1
| #define APP_VERSION ([[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"])
|
判断是否是4寸Retina屏幕
1
| IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
|
让你事倍功半的开发工具
我觉得有一个外接液晶屏幕很重要。。。
此外是一把比较好的椅子。。
此外,推荐大家试试http://revealapp.com/, 一款帮你轻松调试界面的App,收费软件,有试用版本,可以免去改参数-编译-点击查看对应View-改参数-编译的麻烦流程,直接在Reveal里面修改参数就能看到对应效果
iOS Developer账号的那些坑 与 提交审核
iOS Developer 账号
如果希望认真学习开发iOS App,加入iOS开发者计划是必不可少的。当初为了真机调试,又是找破解Xcode的教程又是在淘宝上买开发者证书,最后还是靠亲爱的妈妈特地办了一张Visa信用卡乖乖付钱交了99美元年费。
但是并不是乖乖叫了钱就一番风顺了。创建一个应用,你需要在开发面板上分别配置开发者证书,应用ID,调试设备,推送证书。。。并且绑定对应调试设备和应用ID。。。推送证书还分开发设备的Sandbox环境和Production环境。。。
相关具体设置可以参见苹果开发者账号那些事儿
测试分发
据说TestFlight很不错。。。最近被Apple收购了,我在使用过程中也感到各种不便。不过一些在应用内反馈给测试人员的特性还不错。
最后我发测试包用的是Fir.im,感觉还不错。另外AVOS也提供了相应的功能,值得一试。
同时我也用一个晚上时间做了一个Android APK分发的Demo,放在 Github上,希望有空能完善。