在如今比较流行的MVVM设计模式中,需要有一种双向绑定的机制,在数据模型发生了修改之后, 立即将改变呈现到视图View上去。iOS开发过程中,基于KVO(Key Value Observing)即可实现这种模型和视图的联动机制。
KVO其实是一种观察者模式,利用它可以很容易实现视图View和数据模型的分离,当数据模型的属性值改变之后,作为监听器的视图组件就会触发特定的方法,在该方法中可以获取数据模型变更的数值,从而更新UI。在NSObject类引入了一个名为NSKeyValueObServing的分类(Category),因此所有的Objective-C对象都可以使用KVO。
KVO中常用的方法
使用KVO是,下方的方法是比较常用的。
- 模型对象注册指定KeyPath的监听方法,通常情况下,当模型对象的指定Key发生变化时,通知视图对象。ViewController中的模型对象添加。
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- 视图对象的监听回调方法,在该方法中可以获取数据模型变化前后的数据。视图类中重写这个方法。
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
- 删除指定Key路径的监听器
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
使用KVO实现模型与视图联动的步骤
当需要使用KVO实现模型与视图的联动操作时,可以使用如下步骤进行:
- 创建数据模型对象,并且注册需要监听的KeyPath。
- 在视图类中,实现监听回调方法,即当收到模型对象指定KeyPath发生变化时,对界面UI执行的更新操作。
示例
mode:.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MYMode : NSObject
@property (nonatomic,copy)NSString *name;
@end
NS_ASSUME_NONNULL_END
mode:.m
#import "MYMode.h"
@implementation MYMode
@end
view:.h
#import <UIKit/UIKit.h>
#import "MYMode.h"
NS_ASSUME_NONNULL_BEGIN
@interface MYView : UILabel
@property (nonatomic,strong)MYMode *mode;
@end
NS_ASSUME_NONNULL_END
view:.m
#import "MYView.h"
@implementation MYView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
//重写mode的set方法
- (void)setMode:(MYMode *)mode{
_mode=mode;
self.text=_mode.name;
}
//视图类实现监听回调方法,KVO:模型数据发生变化时调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSString *newText=change[@"new"];
self.text=newText;
NSLog(@"old:%@,new:%@",change[@"old"],change[@"new"]);
}
@end
viewcontroller:.m
#import "ViewController.h"
#import "MYView.h"
#import "MYMode.h"
@interface ViewController ()
@property (nonatomic,strong)MYMode *myMode;
@property (nonatomic,strong)MYView *myView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.myView];
//创建数据模型对象
self.myMode=[[MYMode alloc]init];
//mode 对象添加观察者
[_myMode addObserver:self.myView forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
-(MYView *)myView{
if (_myView==nil) {
_myView=[[MYView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
_myView.backgroundColor=[UIColor redColor];
_myView.textColor=[UIColor whiteColor];
}
return _myView;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.myMode.name=[NSString stringWithFormat:@"%u",arc4random_uniform(10000000)];
}
@end