4.视图层的Self-Manager
最后更新于:2022-04-01 16:18:23
## 视图层的Self-Manager
通常情况下,视图层只是简单负责数据展示和负责将事件响应转交给控制器`C`层执行,创建视图的代码都在控制器层中完成,因此`V`层的状态也不见得比`M`好得多。比如当我自定义一个扇形展开的菜单视图,在点击时的响应:
~~~
//LXDMenuView.m
- (void)clickMenuItem: (LXDMenuItem *)menuItem {
if ([_delegate respondsToSelector: @selector(menuView:didSelectedItem:)]) {
[_delegate menuView: self didSelectedItem: menuItem.tag];
}
}
//ViewController.m
- (void)menuView: (LXDMenuView *)menuView didSelectedItem: (NSInteger)index {
Class controllerCls = NSClassFromString(_controllerNames[index]);
UIViewController *nextController = [[controllerCls alloc] init];
[self.navigationController pushViewController: nextController animated: YES];
}
~~~
这段代码是最常见的`视图->控制器`事件处理流程,当一个控制器界面的自定义视图、控件响应事件过多的时候,即便我们已经使用`#pragma mark -`的方式将这些事件进行分段,但还是会占用过大的代码量。`MVC`公认的问题是`C`完成了太多的业务逻辑,导致过胖,跟`M`层的处理一样的,笔者同样将一部分弱业务转移到`V`层上,比如上面的这段页面跳转:
~~~
@interface LXDMenuView: UIView
@property (nonatomic, strong) NSArray<NSString *> * itemControllerNames;
@end
@implementation LXDMenuView
- (void)clickMenuItem: (LXDMenuItem *)menuItem {
UIViewController *currentController = [self currentController];
if (currentController == nil) { return; }
Class controllerCls = NSClassFromString(_itemControllerNames[menuItem.tag]);
UIViewController *nextController = [[controllerCls alloc] init];
if ([currentController respondsToSelector: @selector(menuView:transitionToController:)]) {
[currentController menuView: self transitionToController: nextController];
}
[currentController.navigationController pushViewController: nextController animated: YES];
}
- (UIViewController *)currentController {
UIResponder *nextResponder = self.nextResponder;
while (![nextResponder isKindOfClass: [UIWindow class]]) {
if ([nextResponder isKindOfClass: [UIViewController class]]) {
return (UIViewController *)nextResponder;
}
nextResponder = nextResponder.nextResponder;
}
return nil;
}
@end
~~~
这种业务转移的思路来自于[开发中的Self-Manager模式](http://blog.sunnyxx.com/2015/12/19/self-manager-pattern-in-ios/)一文。在这种代码结构中,如果`V`层决定了控制器接下来的跳转,那么可以考虑将跳转的业务迁移到`V`中执行。通过[事件链查找](http://www.jianshu.com/p/a8926633837b)的方式获取所在的控制器,这一过程并不能说违背了`MVC`的访问限制原则,在整个过程中`V`不在乎其所在的`currentController`和`nextController`的具体类型,通过自定义一个协议来在跳转前将`nextController`发送给当前控制器完成跳转前的配置。
> 这里要注意的是,Self-Manager有其特定的使用场景。当视图层的回调处理需要两层或者更多的时候,Self-Manager能有效的执行
如果抽离的足够高级,甚至可以定义一个同一个的`Self-Manager`协议来提供给自定义视图完成这些工作。这样同一套业务逻辑可以给任意的自定义视图复用,只要其符合`视图<->控制器`的捆绑关系。:
~~~
@protocol LXDViewSelfManager <NSObject>
@optional
- (void)customView: (UIView *)customView transitionToController: (UIViewController *)nextController;
@end
~~~