美文网首页
iOS应用架构:View层的组织和调用方案(一)

iOS应用架构:View层的组织和调用方案(一)

作者: 彗星来的那一夜 | 来源:发表于2016-06-06 14:27 被阅读29次

    一般来说,一个不够好的View层架构,主要原因有以下五种:

    1.代码混乱不规范

    2.过多继承导致的复杂依赖关系

    3.模块化程度不够高,组件粒度不够细

    4.横向依赖

    5.架构设计失去传承

    这五个地方会影响业务工程师实现需求的效率,进而拖慢迭代周期。View架构的其他缺陷也会或多或少地产生影响,但在我看来这五个是比较重要的影响因素。

    View代码结构的规定

    制定代码规范严格来讲不属于View层架构的事情,但它对View层架构未来的影响会比较大,也是属于架构师在设计View层架构时需要考虑的事情。制定View层规范的重要性在于:

    1.提高业务方View层的可读性可维护性

    2.防止业务代码对架构产生腐蚀

    3.确保传承

    4.保持架构发展的方向不轻易被不合理的意见所左右

    viewController的代码应该差不多是这样:

    要点如下:

    所有的属性都使用getter和setter

    不要在viewDidLoad里面初始化你的view然后再add,这样代码就很难看。在viewDidload里面只做addSubview的事情,然后在viewWillAppear里面做布局的事情(这里在最后还会讨论),最后在viewDidAppear里面做Notification的监听之类的事情。至于属性的初始化,则交给getter去做。

    比如这样:

    #pragma mark - life cycle

    - (void)viewDidLoad

    {

    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:self.firstTableView];

    [self.view addSubview:self.secondTableView];

    [self.view addSubview:self.firstFilterLabel];

    [self.view addSubview:self.secondFilterLabel];

    [self.view addSubview:self.cleanButton];

    [self.view addSubview:self.originImageView];

    [self.view addSubview:self.processedImageView];

    [self.view addSubview:self.activityIndicator];

    [self.view addSubview:self.takeImageButton];

    }

    - (void)viewWillAppear:(BOOL)animated

    {

    [super viewWillAppear:animated];

    CGFloat width = (self.view.width - 30) / 2.0f;

    self.originImageView.size = CGSizeMake(width, width);

    [self.originImageView topInContainer:70 shouldResize:NO];

    [self.originImageView leftInContainer:10 shouldResize:NO];

    self.processedImageView.size = CGSizeMake(width, width);

    [self.processedImageView right:10 FromView:self.originImageView];

    [self.processedImageView topEqualToView:self.originImageView];

    CGFloat labelWidth = self.view.width - 100;

    self.firstFilterLabel.size = CGSizeMake(labelWidth, 20);

    [self.firstFilterLabel leftInContainer:10 shouldResize:NO];

    [self.firstFilterLabel top:10 FromView:self.originImageView];

    ... ...

    }

    这样即便在属性非常多的情况下,还是能够保持代码整齐,view的初始化都交给getter去做了。总

    之就是尽量不要出现以下的情况

    - (void)viewDidLoad

    {

    [super viewDidLoad];

    self.textLabel = [[UILabel alloc] init];

    self.textLabel.textColor = [UIColor blackColor];

    self.textLabel ... ...

    self.textLabel ... ...

    self.textLabel ... ...

    [self.view addSubview:self.textLabel];

    }

    这种做法就不够干净,都扔到getter里面去就好了。

    getter和setter全部都放在最后

    因为一个ViewController很有可能会有非常多的view,就像上面给出的代码样例一样,如果getter和setter写在前面,就会把主要逻辑扯到后面去,其他人看的时候就要先划过一长串getter和setter,这样不太好。然后要求业务工程师写代码的时候按照顺序来分配代码块的位置,先是life cycle,然后是Delegate方法实现,然后是event response,然后才是getters and setters。这样后来者阅读代码时就能省力很多。

    每一个delegate都把对应的protocol名字带上,delegate方法不要到处乱写,写到一块区域里面去

    比如UITableViewDelegate的方法集就老老实实写上#pragma mark - UITableViewDelegate。这样有个好处就是,当其他人阅读一个他并不熟悉的Delegate实现方法时,他只要按住command然后去点这个protocol名字,Xcode就能够立刻跳转到对应这个Delegate的protocol定义的那部分代码去,就省得他到处找了。

    event response专门开一个代码区域

    所有button、gestureRecognizer的响应事件都放在这个区域里面,不要到处乱放。

    关于private methods,正常情况下ViewController里面不应该写

    不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。对的,正常情况下ViewController里面一般是不会存在private methods的,这个private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。

    ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。

    为什么要这样要求?

    我见过无数ViewController,代码布局乱得一塌糊涂,这里一个delegate那里一个getter,然后ViewController的代码一般都死长死长的,看了就让人头疼。

    定义好这个规范,就能使得ViewController条理清晰,业务方程序员很能够区分哪些放在ViewController里面比较合适,哪些不合适。另外,也可以提高代码的可维护性和可读性。

    关于View的布局

    业务工程师在写View的时候一定逃不掉的就是这个命题。用Frame也好用Autolayout也好,如果没有精心设计过,布局部分一定惨不忍睹。

    直接使用CGRectMake的话可读性很差,光看那几个数字,也无法知道view和view之间的位置关系。用Autolayout可读性稍微好点儿,但生成Constraint的长度实在太长,代码观感不太好。

    Autolayout这边可以考虑使用Masonry,代码的可读性就能好很多

    何时使用storyboard,何时使用nib,何时使用代码写View

    这个问题唐巧的博客里这篇文章也提到过,可以参考。

    在这里我还想补充一些内容:

    具有一定规模的团队化iOS开发(10人以上)有以下几个特点:

    同一份代码文件的作者会有很多,不同作者同时修改同一份代码的情况也不少见。因此,使用Git进行代码版本管理时出现Conflict的几率也比较大。

    需求变化非常频繁,产品经理一时一个主意,为了完成需求而针对现有代码进行微调的情况,以及针对现有代码的部分复用的情况也比较多。

    复杂界面元素、复杂动画场景的开发任务比较多。

    是否有必要让业务方统一派生ViewController

    有的时候我们出于记录用户操作行为数据的需要,或者统一配置页面的目的,会从UIViewController里面派生一个自己的ViewController,来执行一些通用逻辑。比如天猫客户端要求所有的ViewController都要继承自TMViewController。这个统一的父类里面针对一个ViewController的所有生命周期都做了一些设置,至于这里都有哪些设置对于本篇文章来说并不重要。在这里我想讨论的是,在设计View架构时,如果为了能够达到统一设置或执行统一逻辑的目的,使用派生的手段是有必要的吗?

    我觉得没有必要,为什么没有必要?

    使用派生比不使用派生更容易增加业务方的使用成本

    不使用派生手段一样也能达到统一设置的目的

    关于在哪儿写Constraints?

    其实在viewWillAppear这里改变UI元素不是很可靠,Autolayout发生在viewWillAppear之后,严格来说这里通常不做视图位置的修改,而用来更新Form数据。改变位置可以放在viewWilllayoutSubview或者didLayoutSubview里,而且在viewDidLayoutSubview确定UI位置关系之后设置autoLayout比较稳妥。另外,viewWillAppear在每次页面即将显示都会调用,viewWillLayoutSubviews虽然在lifeCycle里调用顺序在viewWillAppear之后,但是只有在页面元素需要调整时才会调用,避免了Constraints的重复添加。

    综合各种测试和各种文档,我现在觉得还是在viewDidLoad里面开一个layoutPageSubviews的方法,然后在这个里面创建Constraints并添加,会比较好。就是像下面这样:

    - (void)viewDidLoad

    {

    [super viewDidLoad];

    [self.view addSubview:self.firstView];

    [self.view addSubview:self.secondView];

    [self.view addSubview:self.thirdView];

    [self layoutPageSubviews];

    }

    - (void)layoutPageSubviews

    {

    [self.view addConstraints:xxxConstraints];

    [self.view addConstraints:yyyConstraints];

    [self.view addConstraints:zzzConstraints];}

    相关文章

      网友评论

          本文标题:iOS应用架构:View层的组织和调用方案(一)

          本文链接:https://www.haomeiwen.com/subject/mrnrdttx.html