Ekulelu's Blog

UIViewController瘦身的一些思考

在iOS开发中,界面主要涉及的类有两种:UIViewController和UIView。其中,每个UIViewController都默认包含了一个UIView。
MVC开发模式是主流的客户端编程模式。但是iOS提供的UIViewController似乎把View和Control混合在了一起。事实上也是如此,开发者可以在UIViewContrller里面得到view,进而对view进行各种操作,比如添加子视图,改变控件布局和状态等等。所以MVC进而变成了MV,Controller里面一不小心就会集合了视图控制、逻辑处理和业务相关的大部分代码,导致一个Controller显得特别臃肿。为此,必须将一些代码从UIViewController剥离出去。首先要意识到,MVC框架中,Controller的代码往往是不容易复用的。
下面是我对UIViewController瘦身的一些思考:

1、剥离业务层

业务层逻辑一般比较多,必须抽出另外的类进行处理,UIViewController只负责传递参数和调用,具体的实现逻辑不比理会。这一点开发者都很容易注意到。

2、剥离视图的展示的逻辑

UIViewController应该只负责UIView和Model层间的转发,不应该涉及到控件的布局和控件状态的显示控制。

如果按照上面的两点来编写UIViewController,那么它里面涉及到的事情应该剩下:

  1. 响应用户事件。
  2. 响应业务事件。
  3. 响应系统的各种状态事件,比如网络中断等。
  4. 将上面的事件转发给业务层或UIView。

这样的UIViewController就是一个纯粹的Controller。当然,因为iOS系统的设计原因,UIViewController之间的跳转经常会在UIViewController里面进行,所以会多了一项页面切换的任务。

为了实现上面的要求,需要一些辅助的类。比如系统的联网状态,现在一般是采用监听通知的方式进行,但还得进行一些处理。你当然可以把这些监听写在UIViewController里面,但是这并不是好事情,因为这部分代码是容易复用的,所以我把这部分的业务抽出了一个网络类,它负责将网络状态的改变发送通知出去。其他的各种业务相关的代码也必须新建类进行处理。UIViewController里面绝对不能出现业务逻辑代码。

为了剥离view的布局和显示,从UIViewController里面抽出了一个ViewManager。一个UIViewController对应一个ViewManager。ViewManager负责了该UIViewController里面的视图的布局和展示。布局这部分代码和容易编写,问题在于用户事件的绑定和视图的状态更新。

对用户事件的绑定的问题,有两种方式处理:

  1. 将子控件写在ViewManager的头文件中,暴露给UIViewController,从而UIViewController可以得到子控件进行事件的绑定。

  2. 子控件的用户事件响应函数绑定到ViewManager中,ViewManager再针对每个可以响应用户输入的控件定制一个回调block,由UIViewController去实现这个block。

对于视图的状态更新的问题,也有两种方式:

  1. 将子控件写在ViewManager的头文件中,暴露给UIViewController,UIViewController按照状态直接对子控件状态更新。但是这种方式好像是违背了刚刚说的UIViewController不应该处理控件状态的显示的原则。但是,在有些情况下控件的状态就是和UIViewController的状态绑定在一起的。UIViewController给ViewManager传递状态实际就是给控件的属性直接赋值。如果再包装多一层的状态显得有些多余。当然,这种方式仅对于需要更新状态的控件很少的情况下进行使用。

  2. ViewManager针对控件的需要显示的状态数目,定义几种状态的枚举值。UIViewController仅根据业务的状态给ViewManager的状态赋值,ViewManager根据所给的状态对控件进行更新。对于需要更新的控件多的情况下,建议使用这种方式,代码会更加集中在状态的setter方法里面,UIViewController的代码会更少。