ReactiveCocoa汇总4 MVVM架构

作者: 打电话记错号码的人 | 来源:发表于2016-08-01 17:05 被阅读329次

    作品链接:http://www.jianshu.com/users/1e0f5e6f73f6/top_articles

    1.介绍MVVM架构思想

    1 程序为什么要架构:便于程序员开发和维护代码。

    2 常见的架构思想:

    3 MVVM介绍

    • 模型(M):保存视图数据。

    • 视图+控制器(V):展示内容 + 如何展示

    • 视图模型(VM):处理展示的业务逻辑,包括按钮的点击,数据的请求和解析等等。

    2.ReactiveCocoa + MVVM 登录界面

    1.需求

    • 1.监听两个文本框的内容,有内容才允许按钮点击
    • 2.默认登录请求.

    2.分析

    • 1.界面的所有业务逻辑都交给控制器做处理
    • 2.在MVVM架构中把控制器的业务全部搬去VM模型,也就是每个控制器对应一个VM模型.

    3.步骤

            1.创建LoginViewModel类,处理登录界面业务逻辑.
            2.这个类里面应该保存着账号的信息,创建一个账号Account模型
            3.PHLoginViewModel应该保存着账号信息Account模型。
            4.需要时刻监听Account模型中的账号和密码的改变,怎么监听?
            5.在非RAC开发中,都是习惯赋值,在RAC开发中,需要改变开发思维,由赋值转变为绑定,可以在一开始初始化的时候,就给Account模型中的属性绑定,并不需要重写set方法。
            6.每次Account模型的值改变,就需要判断按钮能否点击,在VM模型中做处理,给外界提供一个能否点击按钮的信号.
            7.这个登录信号需要判断Account中账号和密码是否有值,用KVO监听这两个值的改变,把他们聚合成登录信号.
            8.监听按钮的点击,由VM处理,应该给VM声明一个RACCommand,专门处理登录业务逻辑.
            9.执行命令,把数据包装成信号传递出去
            10.监听命令中信号的数据传递
            11.监听命令的执行时刻
    

    4.代码实现

    4.1 控制器的代码

    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UITextField *accountField;
    @property (weak, nonatomic) IBOutlet UITextField *pwdField;
    @property (weak, nonatomic) IBOutlet UIButton *loginBtn;
    @property (nonatomic, strong) PHLoginViewModel *phLoginVM;
    @end
    
    @implementation ViewController
    
    - (PHLoginViewModel *)phLoginVM
    {
        if (_phLoginVM == nil) {
            _phLoginVM = [[PHLoginViewModel alloc] init];
        }
        return _phLoginVM;
    }
    
    // MVVM:
    // VM:视图模型,处理界面上所有业务逻辑
    // 每一个控制器对应一个VM模型
    // VM:最好不要包括视图V
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        [self phBindViewModel];
    
        [self phLoginEvent];
    }
    
    // 绑定viewModel
    - (void)phBindViewModel
    {
        // 1.给视图模型的账号和密码绑定信号
        RAC(self.phLoginVM, account) = _accountField.rac_textSignal;
        RAC(self.phLoginVM, pwd) = _pwdField.rac_textSignal;
    
    }
    
    // 登录事件
    - (void)phLoginEvent
    {
        // 1.处理文本框业务逻辑
        // 设置按钮能否点击
        RAC(_loginBtn, enabled) = self.phLoginVM.phLoginEnableSiganl;
    
        // 2.监听登录按钮点击
        [[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
           // 处理登录事件
            [self.phLoginVM.phLoginCommand execute:nil];
        }];
    }
    
    
    

    4.2 VM的代码

    • VM.h的代码
    // 保存登录界面的账号和密码
    @property (nonatomic, strong) NSString *account;
    @property (nonatomic, strong) NSString *pwd;
    
    // 处理登录按钮是否允许点击
    @property (nonatomic, strong, readonly) RACSignal *phLoginEnableSiganl;
    
    ///** 登录按钮命令 */
    @property (nonatomic, strong, readonly) RACCommand *phLoginCommand;
    
    • VM.m的代码
    - (instancetype)init
    {
        if (self = [super init]) {
            [self setup];
        }
        return self;
    }
    
    // 初始化操作
    
    - (void)setup
    {
        //1.处理登录点击的信号
        _phLoginEnableSiganl = [RACSignal combineLatest:@[RACObserve(self, account),RACObserve(self, pwd)] reduce:^id(NSString *account, NSString *pwd){
    
            return @(account.length && pwd.length);
        }];
    
        //2.处理登录点击命令
        _phLoginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
            // block:执行命令就会调用
            // block作用:事件处理
            // 发送登录请求
            NSLog(@"发送登录请求");
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    // 发送数据
                    [subscriber sendNext:@"请求登录数据"];
                    [subscriber sendCompleted];
                });
                return nil;
            }];
        }];
    
        //3.处理登录请求返回的结果
        [_phLoginCommand.executionSignals.switchToLatest subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
    
        //4.处理登录执行过程
        [[_phLoginCommand.executing skip:1] subscribeNext:^(id x) {
    
            if ([x boolValue] == YES) {
                // 正在执行
                NSLog(@"正在执行");
                // 显示蒙版
                [MBProgressHUD showMessage:@"正在登录ing......"];
            } else {
                // 执行完成
                // 隐藏蒙版
                [MBProgressHUD hideHUD];
                NSLog(@"执行完成");
            }
        }];
    }
    
    

    3.ReactiveCocoa + MVVM 网络请求数据

    1.需求

    2.分析

    • 请求一样,交给VM模型管理

    3.步骤

            1.控制器提供一个视图模型(PHRequesViewModel),处理界面的业务逻辑
            2.VM提供一个命令,处理请求业务逻辑
            3.在创建命令的block中,会把请求包装成一个信号,等请求成功的时候,就会把数据传递出去。
            4.请求数据成功,应该把字典转换成模型,保存到视图模型中,控制器想用就直接从视图模型中获取。
            5.假设控制器想展示内容到tableView,直接让视图模型成为tableView的数据源,
              把所有的业务逻辑交给视图模型去做,这样控制器的代码就非常少了。
    

    4.控制器的代码

    @interface ViewController ()
    /** 请求视图模型 */
    @property (nonatomic, strong) PHRequestViewModel *phRequestVM;
    @end
    
    @implementation ViewController
    
    - (PHRequestViewModel *)phRequestVM
    {
        if (_phRequestVM == nil) {
            _phRequestVM = [[PHRequestViewModel alloc] init];
        }
        return _phRequestVM;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // 创建tableView
    
        UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
        tableView.dataSource = self.phRequestVM;
        self.phRequestVM.tableView = tableView;
        [self.view addSubview:tableView];
    
        // 执行请求
        [self.phRequestVM.phRequestCommand execute:nil];
    }
    
    

    5.VM的代码

    @interface PHRequestViewModel : NSObject<UITableViewDataSource>
    
    // 请求命令
    @property (nonatomic, strong, readonly) RACCommand *phRequestCommand;
    
    // 模型数组
    @property (nonatomic, strong, readonly) NSArray *models;
    // 控制器中的view
    @property (nonatomic, weak) UITableView *tableView;
    @end
    
    @implementation PHRequestViewModel
    - (instancetype)init
    {
        if (self = [super init]) {
            [self setup];
        }
        return self;
    }
    
    - (void)setup
    {
        _phRequestCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
            // 执行命令
            // 发送请求
    
            // 创建信号
            RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
               // 创建请求管理者
                AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
                [manager GET:@"https://api.douban.com/v2/music/search" parameters:@{@"q":@"经典"} success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
                    // 请求成功的时候调用
                    NSLog(@"%@",responseObject);
                    // 输出plist文件
                    [responseObject writeToFile:@"/Users/apple/Desktop/yinyue.plist" atomically:YES];
    
                    // 请求成功的时候调用
    
                    [subscriber sendNext:responseObject];
                    [subscriber sendCompleted];
                } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
    
    
                }];
    
                return nil;
            }];
    
             // 在返回数据信号时,把数据中的字典映射成模型信号,传递出去
            return [signal map:^id(NSDictionary *value) {
                NSMutableDictionary *dictArr = value[@"musics"];
    
                 // 字典转模型,遍历字典中的所有元素,全部映射成模型,并且生成数组
                NSArray *modelArr = [[dictArr.rac_sequence map:^id(id value) {
    
                    return [PHMusic ph_MusicWithDict:value];
                }] array];
                return modelArr;
            }];
        }];
    
        // 获取请求的数据
    
        [_phRequestCommand.executionSignals.switchToLatest subscribeNext:^(id x) {
           // 有了新数据
            _models = x;
            // 刷新表格
            [self.tableView reloadData];
        }];
    
    }
    
    # pragma mark - UITableViewDataSource
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.models.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
        }
    
        PHMusic *music = self.models[indexPath.row];
        cell.textLabel.text = music.title;
        cell.imageView.image = [UIImage imageNamed:music.image];
    
        return cell;
    }
    

    相关文章

      网友评论

      本文标题:ReactiveCocoa汇总4 MVVM架构

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