一、你可能遇到过的问题
Question 1.使用UITextField时;
当你想时时监测textField.text的变化,以获取最新的text,你是用这样的代理方法吗?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.textField) {
NSString *futureStr = [NSString stringWithFormat:@"%@%@", textField.text, string];
if ([futureStr isEqualToString:@"000"]) {
NSLog(@"----------The text value is 000----------");
}
}
return YES;
}
Question 2.使用UIWebView或WKWebView时;
由于网页端用Ajax,导致我们并不能用常规的网页加载代理方法来检测webView的title的值~比如这样
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if (webView.title.length > 0) {
self.title = webView.title;
}
}
二、这里我们就需要用上观察者模式了
KVO,即key-value-observing,利用一个key来找到某个属性并监听其值得改变。其实这也是一种典型的观察者模式。
- 1,添加观察者
- 2,在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:(因为这个方法属于NSObject,所以,可以用于检测任何对象的任何属性的变化)
- 3,移除观察者
/**
用万能的User类来煮个栗子~
*/
- (void)addObserverForUserName {
User *user = [[User alloc] init];
/* Whether the change dictionaries sent in notifications
should contain NSKeyValueChangeNewKey and NSKeyValueChangeOldKey entries, respectively.
-------------------------------------------------------------
NSKeyValueObservingOptionNew = 0x01,新值
NSKeyValueObservingOptionOld = 0x02,旧值
*/
[user addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
}
/**
监测 user 的 name 属性 发生变化的情况
@param keyPath 被监听的属性
@param object 被监听的对象
@param change 新值和旧值
@param context 附加的额外数据
*/
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"name"] && [object isEqual:user]) {
// Do Something
NSLog(@"----------user'name has changed----------");
}
}
- (void)dealloc {
[user removeObserver:self forKeyPath:@"name"];
}
三、解决上述及类似的问题
Answer 1
[self.textField addTarget:self action:@selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
- (void)textFieldDidChange:(UITextField *)textField {
// TODO
if ([textField.text isEqualToString:@"000"]) {
NSLog(@"----------The text value is 000----------");
}
}
Answer 2
/**
为webView配置监听者
*/
- (void)configWebViewObserver {
[webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
[webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"title"] && [object isEqual:webView]) {
self.title = self.webView.title;
} else if ([keyPath isEqualToString:@"estimatedProgress"] && [object isEqual:webView]) {
self.progressView.progress = self.webView.estimatedProgress;
}
}
- (void)dealloc {
[self.webView removeObserver:self forKeyPath:@"title"];
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
附:再来一发监听UITableView的contentOffset
- (void)configObserverForTableView {
[tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"contentOffset"] && object == tableView) {
CGFloat offsetY = self.contentOffset.y;
CGFloat offsetX = self.contentOffset.x;
NSLog(@"----------offsetX = %f, offsetY = %f----------", offsetX, offsetY);
}
}
- (void)dealloc {
[tableView removeObserver:self forKeyPath:@"contentOffset"];
}
看到这里大家一定就能明白观察者模式的基本套路了吧,心里突然响起了音乐:"其实很简单,其实很自然"~
四、写在最后
- 此方法可用于监听一切对象的属性变化,包括系统的类(UITableView、UIWebView等)和自定义的类(User、Car等)
- 上面的User类是通例,属于基本模型,实际情况有需求而定
- 还可以用于监听动画的特殊时刻,比如:frame、color等变化
- 一定记得在析构函数里移除监听;
- 一定记得在析构函数里移除监听;
- 一定记得在析构函数里移除监听......