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];
};
加油~










网友评论