iOS团队风格的统一
不知不觉团队已经有了4个iOS开发,大家的代码风格完全不一样,所以每次改起别人的代码就头疼,理解起来不是那么顺畅,如鲠在喉。所以,就开了场分享会,把一些基本调用方法和代码风格统一了一下。
前言
主要参考了:
view层的组织和调用方案
更轻量的View Controllers
整洁的Table View代码
因为每个人的风格不一样,有些地方很难定义哪个好那个坏,但是同样的风格很重要,对团队有很大的好处。这些博客都详细介绍了这样做的原因,我这里就把他们的精髓吸取了,加了些自己的想法,就把格式直接定下来了。
ViewController代码结构

- 所有的属性都使用Lazy Init,并且放在最后。这样既美观,对于数组之类的属性也避免了崩溃
- viewDidLoad:addSubview,configData,这样会很美观
- viewWillAppear:布局,布局这个时候设好处很多,比如我们iPad版类似qq空间,一个VC容器里放两个,frame在WillAppear时在确定,这样复用到iPhone版本就不用修改什么。
设置Nav,TabBar是否隐藏,Status颜色。在WillDisAppear在设回原来的状态,这样就不会影响别人的VC。 - ViewDidAppear:添加Notification监听,在DidDisappear里remove掉。
- 每一个delegate都把对应的protocol名字带上,delegate方法不要到处乱写,写到一块区域里面去
- event response专门开一个代码区域,所有button、gestureRecognizer的响应事件都放在这个区域里面,不要到处乱放
- private/public methods,private methods尽量不要写,可能以后别的地方会用到,做一个模块或者category。
view的布局和写法
在一个VC或者View里,要么全用Masonry,要么全用frame。这个要统一,看起来很美观。
storyboard绝对不用,主要是纯代码结合xib。
有些人说storyboard是未来,是apple力推的。但是它不仅效率低,conflict还多。我们曾经分成很多很多小的storyboard减少conflict,但是最后做iPad版本时,整个布局变掉了,类似QQ空间的风格,它的复用性真的差,最后索性全部纯代码写,然后重做iOS版,几天就搞定了。所以只后就彻底抛弃了storyboard。
一些通用的逻辑或者页面是否使用继承来实现?
尽量不通过继承,这也是设计模式中最常说的多用组合少用继承。
很多情况可以使用category或者delegate来实现。
还有就是AOP,它需要一个拦截器,Mehtod Swizzling是个很好的手段。Aspects是个开源的库,利用Mehtod Swizzling实现拦截的功能。
这样很多功能可以统一处理,代码的侵入性很小。比如打点,自定义导航栏,导航栏回退按钮,cell的箭头的统一的设置等。
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// 如果 swizzling 的是类方法, 采用如下的方式:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(swizzling_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
#pragma mark - Method Swizzling
- (void)swizzling_viewWillAppear:(BOOL)animated {
[self swizzling_viewWillAppear:animated];
if (self.navigationController.viewControllers.count > 1) {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
backButton.frame = CGRectMake(0, 0, 44, 44);
[backButton setTitle:@"" forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:@"back_black_icon"] forState:UIControlStateNormal];
[backButton setImageEdgeInsets:UIEdgeInsetsMake(0, -22, 0, 0)];
[backButton addTarget:self action:@selector(backEvent) forControlEvents:UIControlEventTouchUpInside];
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[leftView addSubview:backButton];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftView];
}
}
MVC,MVVM,胖Model,瘦Model
所有的这些选择,其实就是为了给ViewController减负。难点就是怎么去拆分。通俗点讲就是ViewController代码行数很少,拆分出来的部分能复用,并且逻辑清晰。
viewController的作用就是数据请求,处理数据,显示在View上。
数据请求
数据请求是指从服务端或者本地文件,数据库取数据,VC不需要知道从哪里取,只需要数据,我们的做法统一是:
ViewController.m
- (void)configData {
[CTPlanDataManager configPlanJsonDataWithPlanId:planId success:^(NSDictionary *dict) {
} failure:^(NSError *error) {
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self configData];
}
CTPlanDataManager.m
- (void)configPlanJsonDataWithPlanId:(NSUInteger) planId
success:(RequestOSSSuccessDictBlock) success
failure:(RequestOSSFailureBlock) failure {
if ([self planJsonFileExistsWithPlanId:planId]) { //判断本地有没有
NSDictionary *dict = [self readPlanJsonFromFileWithPlanId:planId];
if (success) {
success(dict);
}
}
else {
[self downloadPlanJsonFileWithPlanId:planId progress:nil success:^(NSDictionary *dict) { //从阿里云上取
if (success) {
success(dict);
}
} failure:^(NSError *error) {
if (failure) {
failure(error);
}
}];
}
}
处理数据
处理数据的逻辑全部放在model里,通过model直接获取需要展现的数据。
model.h
@property (nonatomic, strong) NSArray<NSString *> *serviceArray; //从服务端获取的
@property (nonatomic, strong) NSArray< NSString *> *handleArray; //model处理过的
model.m
- (void)setServiceArray:(NSArray *) serviceArray {
_serviceArray = serviceArray;
NSMutableArray< NSString *> *handleArray = [[NSMutableArray alloc] init];
for(NSString *value in _serviceArray) {
//一些逻辑处理
handleValue = [value doSomething];
[handleArray addObject:handleValue];
}
_handleArray = handleArray;
}
数据显示
把处理后的数据显示在View上,这个比较容易,主要就是自定义View,只留出初始化方法和赋值方法。
主要需要注意的地方赋值的时候要分离model和view,可以用category来实现赋值函数。
@implementation CTHeaderView (ConfigureForInfor)
- (void)configureForInfor:(CTInfor *) myInfor
{
self.nameTitleLabel.text = myInfor.name;
NSString* date = [self.dateFormatter stringFromDate: myInfor.birthday];
self.dateLabel.text = date;
......
}
@end
UITableview,UICollectionView
这两个View是最常用的比较重的View。比较复杂的UI一般都用到他们。这个时候cell比较多,viewController比较臃肿,所以必须规范。
- dataSource,delegate,UICollectionViewLayout等必须分离出去写
- 在cell内部控制cell的状态。
//点击的反馈
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
.....
self.selectedBackgroundView = self.selectView;
}
//高亮状态的行为
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
......
} else {
......
}
}
- 控制多个Cell类型的写法风格
typedef NS_ENUM(NSUInteger, ProgressCellTag) {
ProgressDateCellTag = kMinTag,
ProgressBlankCellTag,
ProgressTrainNoticeCellTag,
ProgressTimeNoticeCellTag,
ProgressActionCellTag,
};
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (self.dataSource[indexPath.row].integerValue) {
case ProgressActionCellTag:
return [self tableView:tableView actionCellForRowAtIndexPath:indexPath];
break;
case ProgressDateCellTag:
return [self tableView:tableView dateCellForRowAtIndexPath:indexPath];
break;
case ProgressTimeNoticeCellTag:
return [self tableView:tableView timeNoticeCellForRowAtIndexPath:indexPath];
break;
case ProgressTrainNoticeCellTag:
return [self tableView:tableView trainNoticeCellForRowAtIndexPath:indexPath];
break;
case ProgressBlankCellTag:
return [self tableView:tableView blankCellForRowAtIndexPath:indexPath];
break;
default:
break;
}
return nil;
}
#pragma mark - Cell Getter
- (UITableViewCell *)tableView:(UITableView *)tableView actionCellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
}
- (UITableViewCell *)tableView:(UITableView *)tableView dateCellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
}
- (UITableViewCell *)tableView:(UITableView *)tableView timeNoticeCellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
}
- (UITableViewCell *)tableView:(UITableView *)tableView trainNoticeCellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
}
- (UITableViewCell *)tableView:(UITableView *)tableView blankCellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
}
总结
统一的风格和方式,使我们的逻辑更加清晰。尤其是改别人的代码时,定位问题非常快,只需要理解他的处理逻辑,基本上就是改自己的代码。
iOS团队风格的统一的更多相关文章
- iOS团队代码规范
iOS团队代码规范 工程之始可能需要的工具: 1.使用CocoaPods类库管理工具.CocoaPods安装和使用教程. 2.下载安装注释插件VVDocumenter-Xcode. 一.项目结构管理 ...
- IOS 代码风格习惯 总结1
从我大三下学期开始工作开始, 几乎都是孤独的开发 因为身边开发ios 不多 ,除了学习开源的代码优秀风格技巧 剩下的 就是自己造, 所以 养成了 好多不好的习惯. 本知道面向对象的好处 ,但是实际开 ...
- Android iOS Dribbble风格边栏菜单实现
随着IOS7的推出,大量移动应用也开始进行了重新设计.,开始应用大量的扁平化.可以说现在IOS和Android的风格设计方面确实是在逐渐地靠拢. ReisdeMenu 创意灵感来自于Dribbble( ...
- HihoCoder - 1501:风格不统一如何写程序
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi写程序时习惯用蛇形命名法(snake case)为变量起名字,即用下划线将单词连接起来,例如:file_name. ...
- IOS 7 风格Checkbox
Switchery Switchery is a simple component that helps you turn your default HTML checkbox inputs into ...
- iOS团队开发者测试
那么你需要在你下载证书的那个电脑上从钥匙串-->选择证书-->右键到处证书,保存为.p12的证书,以后这个证书拷贝到任何电脑上去都是可以使用的! 本来只有一台电脑可以测试, 现在要团队开发 ...
- 在DrawingVisual上绘制圆形的进度条,类似于IOS系统风格。
1.说明:在WPF中,文件下载时需要显示下载进度,由于系统自带的条型进度条比较占用空间,改用圆形的进度条,需要在DrawingVisual上呈现. 运行的效果如图: private Point Get ...
- 实现IOS圆角风格的列表ListView
这段代码目前已经加在我的一个jar包androidkit中,还没发布. 适用于android1.6以上,不依赖其他jar包 使用时不需要继承这里的RoundListAdapter.只需要在你实现了Li ...
- IOS团队开发之——CocoaPods 第三方库管理工具
使用前需要下载ruby 的gem 命令镜像,mac 下自带有.但一般不用,直接访问国外网站有限制. 下面安装 http://ruby.taobao.org/ http://blog.devtang.c ...
随机推荐
- 创建第一个 vlan network "vlan100" - 每天5分钟玩转 OpenStack(94)
上一节我们在 ML2 配置中 enable 了 vlan network,今天将创建 vlan100 并讨论底层网络变化. 打开菜单 Admin -> Networks,点击 “Create N ...
- Vim 快速上手
1.vi的基本概念 基本上vi可以分为三种状态,分别是 命令模式(command mode) 插入模式(Insert mode) 底行模式(last line mode) 1) 命令行模式comman ...
- OWIN 中 K Commands(OwinHost.exe)与 Microsoft.AspNet.Hosting 的角色问题
问题详情:K Commands(OwinHost.exe)是不是 OWIN 中的 Host 角色?如果是,那 Microsoft.AspNet.Hosting 对应的是 OWIN 中的哪个角色? OW ...
- 初试JqueryEasyUI(附Demo)
写在前面 准备 布局Layout 菜单树Tree 内容页Tabs 右键菜单Menu 表单Form 对话框Dialog 示例Demo下载 关于easyui不多说,对于我们这样没有美术功底的程序员来说,简 ...
- 关于一道数据库例题的解析。为什么σ age>22 (πS_ID,SCORE (SC) ) 选项是错的?
本人大二学子.近段时间在做数据库复习题的时候遇到一道题,如下. 有关系SC(S_ID,C_ID,AGE,SCORE),查找年龄大于22岁的学生的学号和分数,正确的关系代数表达式是( ) . ⅰ. πS ...
- Oracle软件安装目录满的清理方法
这是Oracle数据库日常运维中很常见的一个场景,安装目录满有时不光会导致无法记录最新数据库的日志信息,导致遇到问题无法查到最新的日志信息,还会引发一些奇怪的问题. 所以日常巡检要保证Oracle的安 ...
- 关于一道PHP面试题的解法
参照一个int型数组,如int[] a1=new int[]{10,9,10,20,15,3,9,8,7,1,1},编写一个方法,要求输出不重复,且降序的拼接字符串(连接字符用逗号),如上数组,输出的 ...
- 数据结构Java实现01----算法概述
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- super.getClass()方法调用
下面程序的输出结果是多少?import java.util.Date;public class Test extends Date{public static void main(String[] a ...
- 第 31 章 项目实战-PC 端固定布局[5]
学习要点: 1.底部区域 2.说明区域 3.版权及证件区 主讲教师:李炎恢 本章主要开始使用学习用 HTML5 和 CSS3 来构建 Web 页面,第一个项目采用 PC 端固定布局来实现. 一.底部区 ...