KVO全称为Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。
实现KVO
- 注册监听
- 移除监听
- 接受通知
我们一为WKWebView添加一个网页请求进度加载条为例。实现一个简单的观察逻辑。
我们首先完成简单的UI逻辑。
//
// DetailViewController.m
// KVO
//
// Created by Ryan on 2020/3/12.
// Copyright © 2020 Ryan. All rights reserved.
//
#import "DetailViewController.h"
#import <WebKit/WebKit.h>
@interface DetailViewController ()
@property (strong, nonatomic, readwrite) WKWebView *webView;
@property (strong, nonatomic, readwrite) UIButton *button;
@property (strong, nonatomic, readwrite) UIProgressView *progressView;
@end
@implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 添加顶部栏左部button
[self.view addSubview:({
self.button = [[UIButton alloc] initWithFrame:CGRectMake(10, 40, 50, 50)];
[self.button setImage:[UIImage systemImageNamed:@"return"] forState:UIControlStateNormal];
[self.button addTarget:self action:@selector(dismissVC) forControlEvents:UIControlEventTouchUpInside];
self.button;
})];
// 添加进度条
[self.view addSubview:({
self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 90, self.view.frame.size.width, 10)];
self.progressView;
})];
// 添加webView
[self.view addSubview:({
self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.height - 100)];
self.webView;
})];
// 为webView请求百度站点
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com/"]]];
}
// VC的dissMiss
- (void)dismissVC {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
在WKWebView的代码页面我们可以发现具有一些可KVO的属性,代表我们可以为WKWebView注册观察者,来观察其KVO属性,来实现一些业务逻辑。
/*! @abstract The page title.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
@property (nullable, nonatomic, readonly, copy) NSString *title;
/*! @abstract The active URL.
@discussion This is the URL that should be reflected in the user
interface.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
@property (nullable, nonatomic, readonly, copy) NSURL *URL;
/*! @abstract A Boolean value indicating whether the view is currently
loading content.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
/*! @abstract An estimate of what fraction of the current navigation has been completed.
@discussion This value ranges from 0.0 to 1.0 based on the total number of
bytes expected to be received, including the main document and all of its
potential subresources. After a navigation completes, the value remains at 1.0
until a new navigation starts, at which point it is reset to 0.0.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
@property (nonatomic, readonly) double estimatedProgress;
注册监听
estimatedProgress属性显示当前页面加载进度,可以用于我们所需要的进度条更新。我们为webView注册观察者,在viewDidLoad添加一些代码
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
- self作为监听者,接受事件
- 监听self.webView的estimatedProgress属性
- 在options:NSKeyValueObservingOptionNew的时候发出通知
移除通知
注册的观察者需要手动在dealloc时移除观察者对象。我们在DetailViewController的扩展方法中添加以下代码:
-(void)dealloc {
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
接受通知
需要实现该方法。
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
我们在扩展方法中添加以下代码,简单的将webView的estimatedProgress属性赋给progressView的progress属性,实现页面加载进度的更新传递。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
self.progressView.progress = self.webView.estimatedProgress;
}
这样我们就实现了一个简单的进度条显示web页面加载进度逻辑。
//
// DetailViewController.m
// KVO
//
// Created by Ryan on 2020/3/12.
// Copyright © 2020 Ryan. All rights reserved.
//
#import "DetailViewController.h"
#import <WebKit/WebKit.h>
@interface DetailViewController ()
@property (strong, nonatomic, readwrite) WKWebView *webView;
@property (strong, nonatomic, readwrite) UIButton *button;
@property (strong, nonatomic, readwrite) UIProgressView *progressView;
@end
@implementation DetailViewController
-(void)dealloc {
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:({
self.button = [[UIButton alloc] initWithFrame:CGRectMake(10, 40, 50, 50)];
[self.button setImage:[UIImage systemImageNamed:@"return"] forState:UIControlStateNormal];
[self.button addTarget:self action:@selector(dismissVC) forControlEvents:UIControlEventTouchUpInside];
self.button;
})];
[self.view addSubview:({
self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 90, self.view.frame.size.width, 10)];
self.progressView;
})];
[self.view addSubview:({
self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.height - 100)];
self.webView;
})];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com/"]]];
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)dismissVC {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
self.progressView.progress = self.webView.estimatedProgress;
}
@end