OC架构00:MVVM双向数据绑定

张建 lol

MVVM 是什么?

MVVM 包含三部分:

  • Model:模型层,主要是存放数据

  • View:视图层,UI绘制

  • ViewModel:业务逻辑处理层,将 ModelView 分开,可以取出 Model 的数据,同时处理 View 要展示的内容涉及的业务逻辑

  • MVVM 采用双向数据绑定:

MVVM 优劣

  • 优势

    • 代码清晰:ViewModel 分离大部分 VC 代码
    • 方便测试:可对 ViewModel 构造单元测试
    • 开发解耦:一位开发者负责逻辑实现,一位开发者负责 UI 实现
  • 劣势

    • 代码量多,需要对每个 Controller 实现绑定

实战思路

  • VC 下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "ZJTwoViewController.h"
#import "ZJTwoViewModel.h"

@interface ZJTwoViewController ()

@property (nonatomic,strong)UIButton * btn;
@property (nonatomic,strong)ZJTwoViewModel * viewModel;
@end

@implementation ZJTwoViewController

- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];;

// 绑定VC
self.viewModel = [[ZJTwoViewModel alloc] initWithVC:self];
}
  • Model下代码
1
2
3
4
5
6
7
@interface ZJTwoModel : NSObject
@property (nonatomic,copy)NSString * name;
@end

@implementation ZJTwoModel

@end
  • View 下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
@protocol ZJTwoViewDelegate <NSObject>
- (void)doSomethings;
@end

@interface ZJTwoView : UIView
// 有用VM
@property (nonatomic,weak)ZJTwoViewModel * viewModel;
// 设置代理
@property (nonatomic,weak)id<ZJTwoViewDelegate>delegate;

// 自身控件
@property (nonatomic,strong)UILabel * label;
@end

.m 文件

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#import "ZJTwoView.h"

@implementation ZJTwoView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.label];
}
return self;
}

#pragma mark -lazy
- (UILabel *)label{
if (!_label) {
_label = [[UILabel alloc] initWithFrame:CGRectMake(20, 40, self.frame.size.width - 40, self.frame.size.height - 80)];
_label.backgroundColor = [UIColor orangeColor];
_label.textAlignment = NSTextAlignmentCenter;
}
return _label;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 代理
if (self.delegate && [self.delegate respondsToSelector:@selector(doSomethings)]) {
[self.delegate doSomethings];
}
}

// 重写 setter 方法,监听来自 viewModel 的数据
- (void)setViewModel:(ZJTwoViewModel *)viewModel{
// 获取数据
_viewModel = viewModel;
/*
实现监听
比如 RAC(好多公司都使用MVVM+RAC两者搭配)
*/
RACSignal * signal = [viewModel rac_valuesForKeyPath:@"name" observer:self];
[signal subscribeNext:^(NSString * name) {
NSLog(@"name=%@",name);
}];
}

@end

  • ViewModel 下代码

.h 文件

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ZJTwoViewModel : NSObject
// 入口方法
- (instancetype)initWithVC:(UIViewController *)controller;
@end

.m 下代码

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#import "ZJTwoViewModel.h"
#import "ZJTwoView.h"
#import "ZJTwoModel.h"

@interface ZJTwoViewModel ()<UITextFieldDelegate,ZJTwoViewDelegate>
// VC
@property (nonatomic,weak)UIViewController * vc;
// Model
@property (nonatomic,strong)ZJTwoModel * model;
// View
@property (nonatomic,strong)ZJTwoView * view;

// TF
@property (nonatomic,strong)UITextField * tf;
// model.name绑定的name
@property (nonatomic,copy)NSString * name;
@end

@implementation ZJTwoViewModel
// 入口方法:绑定一个 VC 并实现业务逻辑
- (instancetype)initWithVC:(UIViewController *)controller{
if (self = [super init]) {
// 绑定控制器
self.vc = controller;
// 初始化UI
[self initUI];
// 初始化RAC
[self initRAC];
}

return self;
}

#pragma mark -initUI
- (void)initUI{
// 添加视图
[self.vc.view addSubview:self.view];

// View和ViewModel双向绑定
self.view.viewModel = self;
// 代理
self.view.delegate = self;

// 模型
self.model = [ZJTwoModel new];
self.model.name = @"ZJ";


// 核心思想 V/M 能够拥有VC,但两者独立,互不影响
// 将Model绑定到VM上
self.name = self.model.name;

// 验证监听:动态检测文本内容
[self.vc.view addSubview:self.tf];
}

#pragma mark -监听
- (void)initRAC{
// 监听视图出现的事件
[[self.vc rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {
NSLog(@"viewWillAppear");
}];
}

#pragma mark -<UITextFieldDelegate>
// 动态检测文本框内容
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

return YES;;
}
// 动态文本
- (void)textFieldDidChange:(id)sender{
UITextField * tf = (UITextField *)sender;
// 变更数据:
// self.name = tf.text;

// 直接改变模型
self.model.name = tf.text;
// 更新UI
self.view.label.text = self.model.name;
}

#pragma mark -ZJTwoViewDelegate
- (void)doSomethings{
NSLog(@"doSomethings");
}

#pragma mark -lazy
- (ZJTwoView *)view{
if (!_view) {
_view = [[ZJTwoView alloc] initWithFrame:CGRectMake(30, 80, kScreenWidth - 60, 200)];
_view.backgroundColor = [UIColor redColor];
}
return _view;
}
- (UITextField *)tf{
if (!_tf) {
_tf = [[UITextField alloc] initWithFrame:CGRectMake(50, 500, kScreenWidth - 100, 50)];
_tf.clearButtonMode = UITextFieldViewModeWhileEditing;
_tf.textColor = [UIColor whiteColor];
_tf.placeholder = @"点击更改";
_tf.backgroundColor = [UIColor redColor];
_tf.delegate = self;
}
return _tf;
}
@end
  • Post title:OC架构00:MVVM双向数据绑定
  • Post author:张建
  • Create time:2023-02-27 21:57:46
  • Post link:https://redefine.ohevan.com/2023/02/27/OC/OC学习00:MVVM双向数据绑定/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
On this page
OC架构00:MVVM双向数据绑定