免费开源的iOS开发学习平台

通过KVO实现模型Model与视图View联动

在如今比较流行的MVVM设计模式中,需要有一种双向绑定的机制,在数据模型发生了修改之后, 立即将改变呈现到视图View上去。iOS开发过程中,基于KVO(Key Value Observing)即可实现这种模型和视图的联动机制。

KVO其实是一种观察者模式,利用它可以很容易实现视图View和数据模型的分离,当数据模型的属性值改变之后,作为监听器的视图组件就会触发特定的方法,在该方法中可以获取数据模型变更的数值,从而更新UI。在NSObject类引入了一个名为NSKeyValueObServing的分类(Category),因此所有的Objective-C对象都可以使用KVO。

KVO中常用的方法

使用KVO是,下方的方法是比较常用的。

  • 模型对象注册指定KeyPath的监听方法,通常情况下,当模型对象的指定Key发生变化时,通知视图对象
- (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实现模型与视图的联动操作时,可以使用如下步骤进行:

  1. 创建数据模型对象,并且注册需要监听的KeyPath
  2. 在视图类中,实现监听回调方法,即当收到模型对象指定KeyPath发生变化时,对界面UI执行的更新操作。

示例代码

下面的案例中,创建了一个模型类和一个自定义视图类,当模型类的数值发生变化时,会实时反映到界面UI中。

  • 创建一个Single View Application工程;

  • 定义Person模型类,并添加一个name属性

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
  • 定义MYLabel视图类,继承自UILabel,在MYLabel.h中添加一个Person类型的属性,该属性为MYLabel提供展示的数据
#import <UIKit/UIKit.h>
#import "Person.h"
@interface MYLabel : UILabel
@property (nonatomic, strong) Person *viewData;
@end
  • 在MYlabel.m文件中,重写viewData属性的setter方法,当设置viewData属性时,更新MYLabel上的文字显示
#import "MYLabel.h"
@implementation MYLabel
-(void)setViewData:(Person *)viewData{
    _viewData = viewData; 
    self.text = viewData.name;
}
  • 在MYLabel.m文件中,实现视图对象的监听回调方法,该方法在模型数据发生变化时会调用,此时会把数据模型最新的数据可以从change参数中获取,等获取到最新的数据后,更新MYLabel的文字显示
//KVO:模型数据发生变化时调用
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{  
    NSString *newText = change[@"new"];
    self.text = newText;  
}
  • 在ViewController.m文件中,添加Person类型的属性以及MYLabel类型的属性,并对myLabel属性进行懒加载操作。
#import "MYLabel.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *myPerson;
@property (weak, nonatomic) IBOutlet MYLabel *myLabel;
@end
-(MYLabel *)myLabel{
    if (_myLabel == nil) {
        _myLabel = [[MYLabel alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
        _myLabel.backgroundColor = [UIColor redColor];
        _myLabel.textColor = [UIColor whiteColor];
    }
    return _myLabel;
}
  • 在ViewDidLoad方法中,创建Person类对象,并注册需要监听的KeyPath
- (void)viewDidLoad {
    [super viewDidLoad];
    //添加myLabel
    [self.view addSubview:self.myLabel];
    //创建模型数据对象
    Person *person = [[Person alloc] init];
    self.myPerson = person;
    //添加观察者
    [person addObserver:self.myLabel forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
  • 点击屏幕时,更新Person类对象的值,此时会调用MYLabel类中的observeValueForKeyPath:ofObject:change:context:方法,从而更新MYLabel对象的显示
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.myPerson.name = [NSString stringWithFormat:@"%d",arc4random_uniform(1000000)];
}

示例代码

https://github.com/99ios/5.5