美文网首页
用MVVM模式和ReactiveCocoa(RAC)绑定写一个D

用MVVM模式和ReactiveCocoa(RAC)绑定写一个D

作者: a浮生若梦a | 来源:发表于2019-08-01 10:58 被阅读0次

    下面是用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绑定,监听属性实现,具体详看代码/

    RAC+MVVM-Demo地址

    
    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.png

    MVVM+RAC网络上有好多这里不再阐述。
    MVVM重要的部分是引入了视图模型,并且视图通过某种观察者从视图模型获取更新。

    这里附上MVVM-Demo地址

    直接说项目,如图所示:


    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之间互通。

    这里附上MVVM-Demo地址

    相关文章

      网友评论

          本文标题:用MVVM模式和ReactiveCocoa(RAC)绑定写一个D

          本文链接:https://www.haomeiwen.com/subject/vjstdctx.html