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

    总结

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

  • 相关阅读:
    获取当前具有输入焦点控件的窗口句柄
    C++日志
    IsWindowVisible
    fedora20 播放aiv视频
    nginx 伪静态大于10个参数 $10
    fedora 安装nginx+php+mysql
    限制图片目录有文件的执行权限
    jQuery制作go to top按钮
    centos 6.5 安装阿里云的一键安装包(nginx+php5.4+mysql5.1)
    centos 6.5网卡dhcp不能获得网关
  • 原文地址:https://www.cnblogs.com/stevenfukua/p/6097601.html
Copyright © 2020-2023  润新知