zoukankan      html  css  js  c++  java
  • 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 {
        //
    }
    

    总结

    统一的风格和方式,使我们的逻辑更加清晰。尤其是改别人的代码时,定位问题非常快,只需要理解他的处理逻辑,基本上就是改自己的代码。

  • 相关阅读:
    URAL 1998 The old Padawan 二分
    URAL 1997 Those are not the droids you're looking for 二分图最大匹配
    URAL 1995 Illegal spices 贪心构造
    URAL 1993 This cheeseburger you don't need 模拟题
    URAL 1992 CVS
    URAL 1991 The battle near the swamp 水题
    Codeforces Beta Round #92 (Div. 1 Only) A. Prime Permutation 暴力
    Codeforces Beta Round #7 D. Palindrome Degree hash
    Codeforces Beta Round #7 C. Line Exgcd
    Codeforces Beta Round #7 B. Memory Manager 模拟题
  • 原文地址:https://www.cnblogs.com/stevenfukua/p/6097601.html
Copyright © 2011-2022 走看看