不知不觉团队已经有了4个iOS开发,你们的代码风格彻底不同,因此每次改起别人的代码就头疼,理解起来不是那么顺畅,如鲠在喉。因此,就开了场分享会,把一些基本调用方法和代码风格统一了一下。html
主要参考了:
view层的组织和调用方案
更轻量的View Controllers
整洁的Table View代码
由于每一个人的风格不同,有些地方很难定义哪一个好那个坏,可是一样的风格很重要,对团队有很大的好处。这些博客都详细介绍了这样作的缘由,我这里就把他们的精髓吸收了,加了些本身的想法,就把格式直接定下来了。ios
在一个VC或者View里,要么全用Masonry,要么全用frame。这个要统一,看起来很美观。
storyboard绝对不用,主要是纯代码结合xib。git
有些人说storyboard是将来,是apple力推的。可是它不只效率低,conflict还多。咱们曾经分红不少不少小的storyboard减小conflict,可是最后作iPad版本时,整个布局变掉了,相似QQ空间的风格,它的复用性真的差,最后索性所有纯代码写,而后重作iOS版,几天就搞定了。因此只后就完全抛弃了storyboard。github
尽可能不经过继承,这也是设计模式中最常说的多用组合少用继承。
不少状况可使用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]; } }
全部的这些选择,其实就是为了给ViewController减负。难点就是怎么去拆分。通俗点讲就是ViewController代码行数不多,拆分出来的部分能复用,而且逻辑清晰。设计模式
viewController的做用就是数据请求,处理数据,显示在View上。数组
数据请求是指从服务端或者本地文件,数据库取数据,VC不须要知道从哪里取,只须要数据,咱们的作法统一是:mvc
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直接获取须要展示的数据。app
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来实现赋值函数。mvvm
@implementation CTHeaderView (ConfigureForInfor) - (void)configureForInfor:(CTInfor *) myInfor { self.nameTitleLabel.text = myInfor.name; NSString* date = [self.dateFormatter stringFromDate: myInfor.birthday]; self.dateLabel.text = date; ...... } @end
这两个View是最经常使用的比较重的View。比较复杂的UI通常都用到他们。这个时候cell比较多,viewController比较臃肿,因此必须规范。
//点击的反馈 - (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 { ...... } }
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 { // }
统一的风格和方式,使咱们的逻辑更加清晰。尤为是改别人的代码时,定位问题很是快,只须要理解他的处理逻辑,基本上就是改本身的代码。