用MVVM模式和ReactiveCocoa(RAC)绑定写一个D
下面是用RAC做的另一个小例子
简单功能介绍,RAC+MVVM-Demo地址
RAC
/在LoginView里:/
-
1.用RAC把输入账号和输入密码的TextField和ViewModel的属性(accountStr,passwordStr)进行绑定。(实时监测信号变化)
-
2.用RAC监听(iconUrlStr)根据输入不同账号内容来显示不同头像。(输入内容只有 0, 012, 0123, 01234 这四种图片对应)
-
3.用RAC监听登录按钮可编辑状态。 按钮可点击事件。(事件响应,RACCommand只需执行 - (RACSignal *)execute:(id)input 方法就可以开始并执行)\n\n4.用RAC监听菊花加载显示。(skip:1 方法是跳过第一步的意思)
/在LoginViewModel里面:/
-
1.VM里面头像图片的属性(iconUrlStr)和输入账号的TextField的输入框进行映射绑定。(功能:对图片URL进行处理)
-
2.VM里检测属性(accountStr,passwordStr),用来判断登录按钮是否可以高亮或点击。
-
3.VM里面属性(loginStatusSubject)用来检测登录的状态。
-
4.RACCommand用来实现请求的响应,具体请查看代码。
/Login整理功能:/
-
1.输入框,输入 0, 012, 0123, 01234 这四种数字头像一一对应。
-
2.账号密码判断,必须都为01234,才可以登录成功。
-
3.俩个输入框必须都有输入才可以点击登录按钮。
-
4.登录中显示登录状态。菊花加载显/隐。
/个人信息页面也是通过RAC绑定,监听属性实现,具体详看代码/
1. 函数式响应式编程
2. 流程: 信号产生-->信号订阅-->信号发送-->信号销毁
2. RAC-->KVO,通知,点击手势,按钮的点击事件绑定等
3. TextField属性监听等,Array,Dictionary,遍历等。
4. Map映射对输入的内容进行处理,过滤后再发送信号。
5. Combine 对组合的信号进行绑定。
6. OC语言库 pod 导入是 ReactiveObjC->3.1.0
7. Swift语言库 pod 导入是 ReactiveSwift->4.0.0
8. 还有好多功能等等。
MVVM
MVVM.pngMVVM+RAC网络上有好多这里不再阐述。
MVVM重要的部分是引入了视图模型,并且视图通过某种观察者从视图模型获取更新。
直接说项目,如图所示:
MVVM.png
在viewController中的关系如下
#import "CBViewController.h"
#import "CBView.h"
#import "CBViewModel.h"
@interface CBViewController ()
@property (nonatomic,strong) CBViewModel *aViewModel;
@property (nonatomic,strong) CBView *aView;
@end
@implementation CBViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"MVVM练习";
self.view.backgroundColor = [UIColor whiteColor];
// creat viewModel
_aViewModel = [[CBViewModel alloc] init];
// creat view
_aView = [[CBView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:_aView];
// viewModel get data (example requests the data)
//这里模拟请求数据,获取数据
[self.aViewModel getModelData];
// give the data to the view
// 把获取到的数据,更新到view视图上
[self.aView showView:_aViewModel];
}
@end
在ViewModel中:
-
获取到Model数据之后,把Model的数据和ViewModel的数据绑定到一起,就是说model的属性值改变后,ViewModel属性值也跟随改变。
-
(利用runtime获取属性,利用RAC检测aModel的属性变化)
-
把按钮点击事件逻辑实现到ViewModel来实现。
代码如下:
#import "CBViewModel.h"
#import "CBModel.h"
@interface CBViewModel ()
@property (nonatomic,strong) CBModel *aModel;
@end
@implementation CBViewModel
- (void)getModelData {
_aModel = [[CBModel alloc] init];
_aModel.titleStr = @"个人信息提交";
_aModel.nameStr = @"张小豪";
_aModel.sexStr = @"男";
_aModel.ageStr = @"19";
_aModel.successStr = @"1";
//利用runtime获取属性,把model的属性和ViewModel的属性绑定到一起
unsigned int aCount = 0;
objc_property_t *aProperties = class_copyPropertyList([self.aModel class], &aCount);
for (int i=0; i<aCount; i++) {
objc_property_t aProperty = aProperties[i];
const char *aName = property_getName(aProperty);
NSString *nameStr = [NSString stringWithUTF8String:aName];
if ([nameStr isEqualToString:@"titleStr"]
|| [nameStr isEqualToString:@"nameStr"]
|| [nameStr isEqualToString:@"sexStr"]
|| [nameStr isEqualToString:@"ageStr"]
|| [nameStr isEqualToString:@"successStr"] ) {
// 利用RAC检测aModel的属性变化
@weakify(self);
[[self.aModel rac_valuesAndChangesForKeyPath:nameStr options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) observer:nil] subscribeNext:^(RACTuple * _Nullable x) {
@strongify(self);
if ([nameStr isEqualToString:@"titleStr"]) {
self.aTitle = x.first?:@"";
}else if ([nameStr isEqualToString:@"nameStr"]) {
self.aName = x.first?:@"";
}else if ([nameStr isEqualToString:@"sexStr"]) {
self.aSex = x.first?:@"";
}else if ([nameStr isEqualToString:@"ageStr"]) {
self.aAge = x.first?:@"";
}else if ([nameStr isEqualToString:@"successStr"]) {
self.aSuccess = x.first?:@"";
}
}];
}
}
}
- (void)viewModelBtnClickedAction {
if ([self.aModel.successStr isEqualToString:@"1"]) {
self.aModel.titleStr = @"信息错误";
self.aModel.nameStr = @"xxx";
self.aModel.sexStr = @"xxx";
self.aModel.ageStr = @"xxx";
self.aModel.successStr = @"xxx";
}else {
self.aModel.titleStr = @"个人信息提交";
self.aModel.nameStr = @"张小豪";
self.aModel.sexStr = @"男";
self.aModel.ageStr = @"19";
self.aModel.successStr = @"1";
}
}
@end
在View中:
-
获取到ViewModel数据之后,把ViewModel的数据和View的数据绑定到一起,就是说ViewModel的属性值改变后,View属性值也跟随改变。
-
(利用runtime获取属性,利用RAC检测ViewModel的属性变化)
-
把按钮点击事件逻辑实现到ViewModel来实现。
代码如下:
#import "CBView.h"
@implementation CBView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor orangeColor];
[self viewLayout];
}
return self;
}
- (void)viewLayout {
CGSize mainSize = self.frame.size;
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, 80, mainSize.width-12*2, 30)];
_titleLabel.font = [UIFont boldSystemFontOfSize:16.0];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor whiteColor];
[self addSubview:_titleLabel];
_nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, CGRectGetMaxY(_titleLabel.frame)+30, 100, 30)];
_nameLabel.font = [UIFont systemFontOfSize:16.0];
_nameLabel.textColor = [UIColor whiteColor];
[self addSubview:_nameLabel];
_sexLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, CGRectGetMaxY(_nameLabel.frame)+30, 100, 30)];
_sexLabel.backgroundColor = [UIColor lightGrayColor];
_sexLabel.font = [UIFont systemFontOfSize:16.0];
_sexLabel.textColor = [UIColor whiteColor];
[self addSubview:_sexLabel];
_ageLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, CGRectGetMaxY(_sexLabel.frame)+30, 100, 30)];
_ageLabel.backgroundColor = [UIColor lightGrayColor];
_ageLabel.font = [UIFont systemFontOfSize:16.0];
_ageLabel.textColor = [UIColor whiteColor];
[self addSubview:_ageLabel];
_sureBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_sureBtn.frame = CGRectMake(20, CGRectGetMaxY(_ageLabel.frame)+30, mainSize.width-20*2, 40);
[_sureBtn setTitle:@"-点我刷新数据-" forState:UIControlStateNormal];
[_sureBtn addTarget:self action:@selector(onPrintClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_sureBtn];
_successLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, CGRectGetMaxY(_sureBtn.frame)+50, mainSize.width-30*2, 40)];
_successLabel.font = [UIFont boldSystemFontOfSize:16.0];
_successLabel.textAlignment = NSTextAlignmentCenter;
_successLabel.textColor = [UIColor blueColor];
[self addSubview:_successLabel];
}
- (void)onPrintClick:(UIButton *)sender {
[self.aViemModel viewModelBtnClickedAction];
}
- (void)showView:(CBViewModel *)viewModel {
self.aViemModel = viewModel;
//利用runtime获取属性,把ViewModel的属性和view视图的属性绑定到一起。
unsigned int aCount = 0;
objc_property_t *aProperties = class_copyPropertyList([viewModel class], &aCount);
for (int i=0; i<aCount; i++) {
objc_property_t aProperty = aProperties[i];
const char *aName = property_getName(aProperty);
NSString *nameStr = [NSString stringWithUTF8String:aName];
if ([nameStr isEqualToString:@"aTitle"]
|| [nameStr isEqualToString:@"aName"]
|| [nameStr isEqualToString:@"aSex"]
|| [nameStr isEqualToString:@"aAge"]
|| [nameStr isEqualToString:@"aSuccess"] ) {
// 利用RAC检测viewModel的属性变化
@weakify(self);
[[viewModel rac_valuesAndChangesForKeyPath:nameStr options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) observer:nil] subscribeNext:^(RACTuple * _Nullable x) {
@strongify(self);
if ([nameStr isEqualToString:@"aTitle"]) {
self.titleLabel.text = x.first?:@"";
}else if ([nameStr isEqualToString:@"aName"]) {
self.nameLabel.text = x.first?:@"";
}else if ([nameStr isEqualToString:@"aSex"]) {
self.sexLabel.text = x.first?:@"";
}else if ([nameStr isEqualToString:@"aAge"]) {
self.ageLabel.text = x.first?:@"";
}else if ([nameStr isEqualToString:@"aSuccess"]) {
self.successLabel.text = x.first?:@"";
}
}];
}
}
}
@end
model里面就是定义一些数据,到这里就完成model和viewModel的数据互通,然后viewModel和View之间互通。