iOS团队风格的统一

不知不觉团队已经有了4个iOS开发,你们的代码风格彻底不同,因此每次改起别人的代码就头疼,理解起来不是那么顺畅,如鲠在喉。因此,就开了场分享会,把一些基本调用方法和代码风格统一了一下。html

前言

主要参考了:
view层的组织和调用方案
更轻量的View Controllers
整洁的Table View代码
由于每一个人的风格不同,有些地方很难定义哪一个好那个坏,可是一样的风格很重要,对团队有很大的好处。这些博客都详细介绍了这样作的缘由,我这里就把他们的精髓吸收了,加了些本身的想法,就把格式直接定下来了。ios

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。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];
    }
}

MVC,MVVM,胖Model,瘦Model

全部的这些选择,其实就是为了给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

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 {
    //
}

总结

统一的风格和方式,使咱们的逻辑更加清晰。尤为是改别人的代码时,定位问题很是快,只须要理解他的处理逻辑,基本上就是改本身的代码。

相关文章
相关标签/搜索