美文网首页2018技术笔记
2018笔记——MVVM-C动态绑定

2018笔记——MVVM-C动态绑定

作者: 满庭花醉三千客 | 来源:发表于2018-07-28 11:07 被阅读79次

MVVM中,ViewController持有ViewModel,但是ViewModel不知道ViewController。
这就有一个问题:假如用户在ViewController的textfield中输入内容,则可以直接赋值给ViewModel,完成数据的更新。但是如果ViewModel中有网络请求拿到当前日期,拿到数据后需要去更新VC中的某个label,怎么办呢?

方案1:
在ViewModel.h中新增一个属性:dateString
在ViewController中的使用KVO来监听dateString的变动,该值变化时,我们在KVO的触发方法中,更新VC的label的展示。

ViewController
[self.viewModel addObserver:self forKeyPath:@"dateString" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

//KVO触发的事件
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"dateString"] && [object isEqual:self.viewModel]) {
NSString *newString = [change objectForKey:@"new"];
        NSArray *dateArray = [newString componentsSeparatedByString:@"-"];

        self.yearString = [dateArray objectAtIndex:0];
        self.monthString = [dateArray objectAtIndex:1];
        self.dayString = [dateArray objectAtIndex:2];
        [self.mainTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone];

    }
}

方案2:
在ViewModel.h中新增一个属性:dateString
新建一个CEKVOCenter,可以通过KVO监听值的变动,监听到后,触发block。
在ViewController中的使用CEKVOCenter来监听dateString的变动,该值变化时,我们在CEKVOCenter的block中,更新VC的label的展示。
CEKVOCenter.h

#import <Foundation/Foundation.h>

typedef void(^CEKVOCenterBlock) (NSObject *passiveObserver, NSString *keyPath,NSDictionary *change, void * context);

@interface CEKVOCenter : NSObject

+ (instancetype)defaultCenter;

- (void)addPassiveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath withBlock:(CEKVOCenterBlock)block;


- (void)addPassiveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context withBlock:(CEKVOCenterBlock)block;

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

@end

CEKVOCenter.m

#import "CEKVOCenter.h"
#import <Foundation/Foundation.h>
@interface CEKVOCenter()
/**
*  触发的block
**/
@property (nonatomic, copy) CEKVOCenterBlock ceBlock;
/**
*   keyPath数组
**/
@property (nonatomic, strong) NSMutableArray *keyArray;
@end

@implementation CEKVOCenter
//单例初始化方法
+ (instancetype)defaultCenter
{
    static CEKVOCenter *center = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        center = [[CEKVOCenter alloc]init];
        center.keyArray = [NSMutableArray array];
    });
    return center;
}
//添加KVO 简版
- (void)addPassiveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath withBlock:(CEKVOCenterBlock)block
{
    [self addPassiveObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil withBlock:block];
}

//完整版KVO
- (void)addPassiveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context withBlock:(CEKVOCenterBlock)block
{
    [observer addObserver:self forKeyPath:keyPath options:options context:context];
    [self.keyArray addObject:[keyPath copy]];
    
    if (block) {
        self.ceBlock = block;
    }
    
}

//KVO触发的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{

    if (self.ceBlock) {
        self.ceBlock(object,keyPath,change,context);
    }
}

//移除KVO
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
    [observer removeObserver:self forKeyPath:keyPath];
}

@end

ViewController.m

[[CEKVOCenter defaultCenter] addPassiveObserver:self.viewModel forKeyPath:@"dateString"  withBlock:^(NSObject *passiveObserver, NSString *keyPath, NSDictionary *change, void *context) {
        
        NSString *newString = [change objectForKey:@"new"];
        NSArray *dateArray = [newString componentsSeparatedByString:@"-"];
        
        self.yearString = [dateArray objectAtIndex:0];
        self.monthString = [dateArray objectAtIndex:1];
        self.dayString = [dateArray objectAtIndex:2];
        [self.mainTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone];
}];

方案3:
在ViewModel.h中新增一个属性:dateString
在ViewModel.h中新增一个属性:dateBlock,回传一个字符串
在ViewModel.m中实现setDateString方法,同时触发dateBlock,把当前的dateString回传。
在ViewController中实现ViewModel的dateBlock,然后在block实现中,更新VC的label的展示。

ViewModel.h

typedef void(^MUCityTourViewModelDateBlock)(NSString *dateString);

/**
*  date
**/
@property (nonatomic, copy) NSString *dateString;

/**
*  dateString变化时触发的block
**/
@property (nonatomic, copy) MUCityTourViewModelDateBlock dateBlock;

ViewModel.m

- (void)setDateString:(NSString *)dateString
{
    _dateString = dateString;
    if (self.dateBlock) {
        self.dateBlock(dateString);
    }
}

ViewController.m

//防止循环引用,设置weakSelf
__weak __typeof(self) weakSelf = self;

self.viewModel.dateBlock = ^(NSString *dateString) {
    __strong __typeof(self) strongSelf = weakSelf;

    NSArray *dateArray = [dateString componentsSeparatedByString:@"-"];
    strongSelf.yearString = [dateArray objectAtIndex:0];
    strongSelf.monthString = [dateArray objectAtIndex:1];
    strongSelf.dayString = [dateArray objectAtIndex:2];
    [strongSelf.mainTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:1]] withRowAnimation:UITableViewRowAnimationNone];
};

加油~

相关文章

网友评论

    本文标题:2018笔记——MVVM-C动态绑定

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