iOS开发攻城狮的集散地ReactiveCocoa研究iOS 开发每天分享优质文章

MVVM和ReactiveObjC实践2018

2018-10-08  本文已影响137人  勇往直前888

尝试在工程中使用MVVMReactiveObjC
引入MVVM是为了将界面和逻辑分开,将界面接口,比如label.text,转化为相应view model的数据属性接口。
引入ReactiveObjC主要是为了优雅地进行Controllerview model之间的双向绑定。
关于ReactiveObjC,这篇文章很有用:
iOS开发之ReactiveCocoa下的MVVM

目标页面

登录模块是基础模块之一。

企业微信截图_055f0b32-c210-4fec-8b5a-5184660c26ad.png 企业微信截图_38ced2ef-74ff-4b01-a311-47a58bbb1715.png

实现方式

企业微信截图_b7237bbf-a3c9-4fce-a8bb-d166224c62e3.png
#import "KJTPasswordLoginChildViewController.h"
#import "KJTCodeLoginChildViewController.h"

@interface KJTLoginViewController ()

// child controller
@property (strong, nonatomic) KJTPasswordLoginChildViewController *passwordController;
@property (strong, nonatomic) KJTCodeLoginChildViewController *codeController;

@end

#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
    if ([segue.identifier isEqualToString:@"toPassword"]) {
        self.passwordController = segue.destinationViewController;
    }
    if ([segue.identifier isEqualToString:@"toCode"]) {
        self.codeController = segue.destinationViewController;
    }
}

引入MVVM

结论:综合考虑,对于这个登录页面,在当前的场景下,引入MVVM是有利的。

文件结构

企业微信截图_b45e7e8f-29ef-49fc-a694-de93a8dd0ce1.png

引入ReactiveObjC

结论:引入ReactiveObjC,建立controllerview model之间的属性绑定,当view model改变的时候,可以优雅地在界面上提现出来。

View Model定义

  1. 文字的颜色,选中是蓝色的,不选中是黑色的;
  2. 指示线的显示和隐藏;
  3. 代表子controllercontainer view的显示和隐藏;
  4. 当前用户选择了那种登录方式;
typedef NS_ENUM(NSInteger,KJTLoginType) {
    KJTLoginTypeCode = 1,
    KJTLoginTypePassword = 2,
};

@interface KJTLoginViewModel : NSObject

/*
 *  KJTLoginViewController
 */

// 登录方式
@property (assign, nonatomic) KJTLoginType loginType;

// 验证码标签颜色
@property (strong, nonatomic) UIColor *codeLabelColor;
// 隐藏验证码标签横线
@property (assign, nonatomic) BOOL hideCodeLine;
// 隐藏验证码登录容器
@property (assign, nonatomic) BOOL hideCodeContainer;

// 账号密码标签颜色
@property (strong, nonatomic) UIColor *passwordLabelColor;
// 隐藏账号密码标签横线
@property (assign, nonatomic) BOOL hidePasswordLine;
// 隐藏账号密码登录容器
@property (assign, nonatomic) BOOL hidePasswordContainer;

/*
 *  KJTPasswordLoginChildViewController
 */


/*
 *  KJTCodeLoginChildViewController
 */

@end
#import "KJTLoginViewModel.h"

@implementation KJTLoginViewModel

#pragma mark - life cycle
- (instancetype)init {
    self = [super init];
    if (self) {
        [self bindLoginType];
        [self setDefaultValue];
    }
    return self;
}

#pragma mark - private
- (void)bindLoginType {
    [RACObserve(self, loginType) subscribeNext:^(id  _Nullable x) {
        KJTLoginType loginType = (KJTLoginType)[x integerValue];
        switch (loginType) {
            case KJTLoginTypeCode:
                self.codeLabelColor = kBlueColor;
                self.hideCodeLine = NO;
                self.hideCodeContainer = NO;
                self.passwordLabelColor = kBlackColor;
                self.hidePasswordLine = YES;
                self.hidePasswordContainer = YES;
                break;
            case KJTLoginTypePassword:
                self.codeLabelColor = kBlackColor;
                self.hideCodeLine = YES;
                self.hideCodeContainer = YES;
                self.passwordLabelColor = kBlueColor;
                self.hidePasswordLine = NO;
                self.hidePasswordContainer = NO;
                break;
            default:
                break;
        }
    }];
}

- (void)setDefaultValue {
    self.loginType = KJTLoginTypePassword;
}

@end

控制器属性

@interface KJTLoginViewController ()
// 验证码登录
@property (weak, nonatomic) IBOutlet UILabel *codeLabel;
@property (weak, nonatomic) IBOutlet UIView *codeLine;
@property (weak, nonatomic) IBOutlet UIView *codeContainer;
// 账号密码登录
@property (weak, nonatomic) IBOutlet UILabel *passwordLabel;
@property (weak, nonatomic) IBOutlet UIView *passwordLine;
@property (weak, nonatomic) IBOutlet UIView *passwordContainer;
// view model
@property (strong, nonatomic) KJTLoginViewModel *viewModel;

@end

绑定View Model

view model的属性变化要反映在界面上,那么就需要建立代表界面的输出口和view model属性之间的绑定工作。有ReactiveObjC的帮助,这将会非常简单:

#pragma mark - private
- (void)bindViewModel {
    self.viewModel = [[KJTLoginViewModel alloc] init];
    RAC(self.codeLabel, textColor) = RACObserve(self.viewModel, codeLabelColor);
    RAC(self.codeLine, hidden) = RACObserve(self.viewModel, hideCodeLine);
    RAC(self.codeContainer, hidden) = RACObserve(self.viewModel, hideCodeContainer);
    RAC(self.passwordLabel, textColor) = RACObserve(self.viewModel, passwordLabelColor);
    RAC(self.passwordLine, hidden) = RACObserve(self.viewModel, hidePasswordLine);
    RAC(self.passwordContainer, hidden) = RACObserve(self.viewModel, hidePasswordContainer);
}

登录方式切换

由于界面的元素输出口已经和view model的属性进行了绑定; view model的其他属性已经和登录方式属性进行了绑定。所以,当用户切换登录方式时,只要修改view model的登录方式属性loginType就可以,简单易懂:

- (IBAction)codeButtonTouched:(id)sender {
    self.viewModel.loginType = KJTLoginTypeCode;
}

- (IBAction)passwordButtonTouched:(id)sender {
    self.viewModel.loginType = KJTLoginTypePassword;
}

运行效果

- (void)setDefaultValue {
    self.loginType = KJTLoginTypePassword;
}
企业微信截图_8ed75d18-9d81-4481-a48c-212b716dda68.png 企业微信截图_43323917-8421-40f4-9317-2e832ca2a294.png

小结: 引入ReactiveObjC之后,以前相对比较繁琐的“tab切换”页面,可以非常简洁的实现。

上一篇 下一篇

猜你喜欢

热点阅读