4.视图层的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模式一文。在这种代码结构中,如果V层决定了控制器接下来的跳转,那么可以考虑将跳转的业务迁移到V中执行。通过事件链查找的方式获取所在的控制器,这一过程并不能说违背了MVC的访问限制原则,在整个过程中V不在乎其所在的currentControllernextController的具体类型,通过自定义一个协议来在跳转前将nextController发送给当前控制器完成跳转前的配置。

这里要注意的是,Self-Manager有其特定的使用场景。当视图层的回调处理需要两层或者更多的时候,Self-Manager能有效的执行

如果抽离的足够高级,甚至可以定义一个同一个的Self-Manager协议来提供给自定义视图完成这些工作。这样同一套业务逻辑可以给任意的自定义视图复用,只要其符合视图<->控制器的捆绑关系。:

@protocol LXDViewSelfManager <NSObject>

@optional
- (void)customView: (UIView *)customView transitionToController: (UIViewController *)nextController;

@end
文章导航