MVVM

来源:互联网 发布:电脑桌面软件怎么删除 编辑:程序博客网 时间:2024/05/02 04:43

目的:便于开发和维护代码

M(Model):数据模型
V(View + Controller): 展示内容 + 如何展示
VM(ViewModel):视图模型,处理展示的业务逻辑,包括按钮的点击,数据的请求和解析

登录功能实现

MVC实现:
这里写图片描述

#import "ViewController.h"#import "ReactiveCocoa.h"#import <MBProgressHUD/MBProgressHUD.h>@interface ViewController ()@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;@property (weak, nonatomic) IBOutlet UITextField *pawwordTextField;@property (weak, nonatomic) IBOutlet UIButton *loginButton;@end@implementation ViewController- (void)viewDidLoad {    // 字段校验(当用户名和密码都不为空的时候,登录按钮才可以点击)-----------------------------------    RACSignal *validateFieldSignal = [RACSignal combineLatest:@[_usernameTextField.rac_textSignal, _pawwordTextField.rac_textSignal] reduce:^id(NSString *username, NSString *password){        return @(username.length && password.length);    }];    RAC(_loginButton, enabled) = validateFieldSignal;    // 按钮点击事件------------------------------------------    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {        NSLog(@"请求接口登录...");        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {            NSLog(@"发送响应的登录数据");            [subscriber sendNext:@"发送响应的登录数据"];            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{                [subscriber sendCompleted];            });            return nil;        }];    }];    [command.executionSignals.switchToLatest subscribeNext:^(id x) {        NSLog(@"接收登录的数据");    }];    // 监听命令的执行过程    [[command.executing skip:1] subscribeNext:^(id x) {        if ([x boolValue] == YES) {            NSLog(@"正在登录ing...");            [MBProgressHUD showHUDAddedTo:self.view animated:YES];        } else {            NSLog(@"登录完成!");            [MBProgressHUD hideHUDForView:self.view animated:YES];        }    }];    [[_loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {        NSLog(@"点击登录按钮");        [command execute:nil];    }];  }@end2016-12-18 11:08:32.704 ReactiveCocoa[47893:7242989] 点击登录按钮2016-12-18 11:09:10.111 ReactiveCocoa[47893:7242989] 请求接口登录...2016-12-18 11:09:13.871 ReactiveCocoa[47893:7242989] 正在登录ing...2016-12-18 11:11:58.765 ReactiveCocoa[47893:7242989] 发送响应的登录数据2016-12-18 11:12:02.722 ReactiveCocoa[47893:7242989] 接收登录的数据2016-12-18 11:12:07.418 ReactiveCocoa[47893:7242989] 登录完成!

代码分析:
1. 程序启动时就会执行聚合信号combineLatest:reduce:,结果是登录按钮不可点击
2. 当执行到创建命令RACCommand时,参数代码块和内部代码块都不执行
3. 当执行内部信号订阅时(command.executionSignals.switchToLatest),订阅的代码块也不执行
4. 命令的执行过程也不会得到执行(因为命令还没开始执行)
5. 按钮事件rac_signalForControlEvents:也不会执行(因为没有点击按钮)
6. 当每输入用户名和密码时都会执行聚合信号,来判断按钮是否可以点击(每当UITextField的文本发生改变,内部就会产生一个信号)
7. 单击登录按钮打印@”点击登录按钮”并执行命令([command execute:nil]),紧接着会执行初始化命令时传的代码块参数signalBlock, 也就打印NSLog(@”请求接口登录…”); 一但命令执行了,那么就会监听到命令的执行过程,接着订阅命令执行过程的代码块,打印NSLog(@”正在登录ing…”); 当signalBlock执行完时会返回一个信号RACSignal,也就是说会产生一个信号,那么从信号源中就能拿到返回的信号并订阅,(command.executionSignals.switchToLatest),一但订阅就会执行命令返回的信号中的代码块,接着打印NSLog(@”发送响应的登录数据”); ,当执行sendNext的时候会发送新号,接着打印NSLog(@”接收登录的数据”);, 当执行过sendCompleted时候就会打印NSLog(@”登录完成!”);


VM:处理界面上的所有业务逻辑,每一个控制器对应一个VM,VM中最好不要包括UI

MVVM实现:

#import <Foundation/Foundation.h>#import "ReactiveCocoa.h"@interface LoginViewModel : NSObject@property (strong, nonatomic) NSString *username;@property (strong, nonatomic) NSString *password;@property (strong, nonatomic, readonly) RACSignal *validateFieldSignal;@property (strong, nonatomic, readonly) RACCommand *loginCommand;@end#import "LoginViewModel.h"#import <MBProgressHUD/MBProgressHUD.h>@implementation LoginViewModel- (instancetype)init {    if (self = [super init]) {        [self setup];    }    return self;}- (void)setup {    _validateFieldSignal = [RACSignal combineLatest:@[RACObserve(self, username), RACObserve(self, password)] reduce:^id(NSString *username, NSString *password){        return @(username.length && password.length);    }];    _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {        NSLog(@"请求接口登录...");        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {            NSLog(@"发送响应的登录数据");            [subscriber sendNext:@"发送响应的登录数据"];            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{                [subscriber sendCompleted];            });            return nil;        }];    }];    [_loginCommand.executionSignals.switchToLatest subscribeNext:^(id x) {        NSLog(@"接收登录的数据");    }];    // 监听命令的执行过程    [[_loginCommand.executing skip:1] subscribeNext:^(id x) {        UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;        if ([x boolValue] == YES) {            NSLog(@"正在登录ing...");            [MBProgressHUD showHUDAddedTo:keyWindow animated:YES];        } else {            NSLog(@"登录完成!");            [MBProgressHUD hideHUDForView:keyWindow animated:YES];        }    }];}@end
#import "ViewController.h"#import "LoginViewModel.h"#import "ReactiveCocoa.h"#import <MBProgressHUD/MBProgressHUD.h>@interface ViewController ()@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;@property (weak, nonatomic) IBOutlet UITextField *pawwordTextField;@property (weak, nonatomic) IBOutlet UIButton *loginButton;@property (strong, nonatomic) LoginViewModel *loginViewModel;@end@implementation ViewController- (void)viewDidLoad {    // 绑定值    RAC(self.loginViewModel, username) = _usernameTextField.rac_textSignal;    RAC(self.loginViewModel, password) = _pawwordTextField.rac_textSignal;    RAC(_loginButton, enabled) = self.loginViewModel.validateFieldSignal;    // 处理事件    [[_loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {        NSLog(@"点击登录按钮");        [self.loginViewModel.loginCommand execute:nil];    }];}- (LoginViewModel *)loginViewModel {    if (_loginViewModel == nil) {        _loginViewModel = [[LoginViewModel alloc] init];    }    return _loginViewModel;}@end

RAC请求接口:


#import "ViewController.h"#import "RequestViewModel.h"#import "ReactiveCocoa.h"@interface ViewController ()@property (strong, nonatomic) RequestViewModel *requestViewModel;@end@implementation ViewController- (void)viewDidLoad {    RACSignal *signal = [self.requestViewModel.requestCommand execute:nil];    [signal subscribeNext:^(id x) {        NSLog(@"%@", x);    }];}- (RequestViewModel *)requestViewModel {    if (_requestViewModel == nil) {        _requestViewModel = [[RequestViewModel alloc] init];    }    return _requestViewModel;}@end
#import "ViewController.h"#import "RequestViewModel.h"#import "ReactiveCocoa.h"@interface ViewController ()@property (strong, nonatomic) RequestViewModel *requestViewModel;@end@implementation ViewController- (void)viewDidLoad {    RACSignal *signal = [self.requestViewModel.requestCommand execute:nil];    [signal subscribeNext:^(id x) {        NSLog(@"%@", x);    }];}- (RequestViewModel *)requestViewModel {    if (_requestViewModel == nil) {        _requestViewModel = [[RequestViewModel alloc] init];    }    return _requestViewModel;}@end#import <Foundation/Foundation.h>#import "ReactiveCocoa.h"@interface RequestViewModel : NSObject@property (strong, nonatomic) RACCommand *requestCommand;@end#import "RequestViewModel.h"#import <AFNetworking/AFNetworking.h>@implementation RequestViewModel- (RACCommand *)requestCommand {    if (_requestCommand == nil) {        _requestCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {            RACSignal *requstSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {                AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];                NSMutableDictionary *parameters = [NSMutableDictionary dictionary];                parameters[@"q"] = @"美女";                [manager GET:@"https://api.douban.com/v2/book/search " parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {                    [subscriber sendNext:responseObject[@"books"]];                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {                }];                return nil;            }];            return requstSignal;        }];    }    return _requestCommand;}@end
0 0
原创粉丝点击