0%

通用组件的可定制化

这两天无意间又看到了iOS 开发中的 Self-Manager 模式,思考了这个问题:

当通用组件既需要有通用又要可定制化并且还要保持干净纯粹该怎么办?

使用这个控件的人只需要调用这个 configure 方法就可以配置入参和事件处理。但随之而来的就是一些蛋疼的问题:

  1. configure 的调用者是 superview,上面的例子中也就是一个 UITableViewCell,但 Cell 这层并不知道自己的 ViewController 是谁,于是乎还得向上一级传递这个点击事件,直到能获取到 NavigationController,然后 Push 一个用户信息的页面。
  2. 这个 Avatar View 在 App 的各个地方都可能粗线,而且行为一致,那就意味着事件处理的 block,要散落在各个页面中,同时也带来了很多“只是为向上一层级转发事件”的 “Middle Man”

Self-Manager 减少了「中间人传递」造成的冗余代码。那有没有更好的做法?

能不能定制化的业务逻辑依旧由VC控制,但同时还要保证统一逻辑不用重复写?

答案是可以的。

ViewController-Notification 模式

【Demo 在这里】

View 通过通知往外传递消息进行解耦,VC 配置 View 的定制化逻辑

img

ViewController 负责配置

目前是创建了一个基类,方便做通知的监听和移除的工作,我们假设一个页面需要引入点击头像跳转或者显示用户照片的功能,那他只要声明头像对应的模块(JFTAvatarJumpModule)就可以了。

JFTAvatarJumpModule 是由底层业务组件(头像)提供的定制化可配置模块

1
2
3
4
5
6
7
8
@implementation ViewController

- (NSArray<NSString *> *)viewControllerVNModules {

return @[@"JFTAvatarJumpModule"];
}

@end

组件提供定制化逻辑模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#import <Foundation/Foundation.h>

#import "VCNViewModuleProtocol.h"

extern NSString *const kJFTAvatarJumpNotificaitonName;

@interface JFTAvatarJumpModule : NSObject<VCNViewModuleProtocol>

@end

NSString *const kJFTAvatarJumpNotificaitonName = @"JFTAvatarJumpNotificaitonName";

@implementation JFTAvatarJumpModule

+ (NSString *)notificationName {

return kJFTAvatarJumpNotificaitonName;

}

+ (void)notificationHandler:(NSDictionary *)userInfo viewController:(UIViewController *)viewController {
if ([userInfo[@"userName"] isEqualToString:@"用户名"]) {
NSLog(@"跳转");
} else {
NSLog(@"显示图片");
}
}

@end

用 category 给原来的 View 增加配置文件

让 View 负责往外发通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface JFTUserInfoView (Config)
- (void)customConfig;
@end
@implementation JFTUserInfoView (Config)
- (void)customConfig {
[self.avatarButton addTarget:self action:@selector(avatarButtonClick:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)avatarButtonClick:(UIButton *)btn {
NSDictionary *userInfo = @{ @"sender" : btn ,
@"userName" : self.userName
};
[self postNotification:kJFTAvatarJumpNotificaitonName userInfo:userInfo];
}

@end

总结

这样做的优势还是比较明显的

1. 依旧能保持 View 的纯洁性

用 category 给 View 添加了配置文件,原来 View 还是能够单独拿出去用

2.重复代码减少

假设有一个评论按钮,可能会有两种逻辑。

  1. 没有登陆先跳转到登陆页面
  2. 弹出输入框评论

这两种逻辑的判断肯定不能写在 View 中。相对合理的做法就是把判断逻辑包装成一个 Block,然后传给 View,可是创建的 block 的相关代码肯定每个 VC 都会有一份。

使用这种模式以后配置 block 的过程被 viewControllerVNModules 声明所代替,接入方便不用拷贝代码。

3.逻辑统一维护方便

如果按照每个组件使用者都自己维护跳转逻辑,将来需要对旧接口进行改造会要求所有业务方一起改接口,相当痛苦。

4.原来的业务可以无痛改造

和 MVC 模式完全不冲突,业务方的老代码只需把定制化的部分都删除,在 VC 中声明所需要配置的定制化逻辑模块VCNViewModuleProtocol