KVO监听WKWebView.scrollView.contentSize重复通知问题咨询
解决WKWebView scrollView.contentSize KVO重复通知的问题
我之前也碰到过这个坑!WKWebView的scrollView在滚动时,哪怕contentSize根本没变化,也会频繁触发KVO通知,这其实是UIKit内部的实现逻辑导致的——KVO的触发判断是是否调用了setter方法,而不是值是否真的发生了改变。系统在滚动过程中,可能会多次调用setContentSize:(哪怕传入的尺寸和当前值完全一样),这就导致了重复通知。
关于你担心的性能开销:完全不用焦虑!
你说的“记录上次尺寸并对比”的方案,其实是处理这类场景的常规操作,而且性能开销几乎可以忽略不计:
- 存储一个
CGSize只是占用两个浮点数的内存,非常轻量; - 对比
CGSize的操作就是简单的数值比较,耗时微乎其微,远低于KVO本身的通知开销。
具体实现示例
下面是Objective-C和Swift的代码示例,帮你过滤掉重复的通知:
Objective-C版本
// 声明一个实例变量存储上次的尺寸 @property (nonatomic, assign) CGSize lastContentSize; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"contentSize"] && object == self.webView.scrollView) { CGSize newSize = [change[NSKeyValueChangeNewKey] CGSizeValue]; // 只有当尺寸真正变化时才处理逻辑 if (!CGSizeEqualToSize(newSize, self.lastContentSize)) { self.lastContentSize = newSize; NSLog(@"ContentSize changed to (%.1f, %.1f)", newSize.width, newSize.height); // 这里添加你的业务逻辑 } } else { // 别忘了调用父类方法处理其他KVO通知 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }
Swift版本
private var lastContentSize: CGSize = .zero override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "contentSize", let scrollView = object as? UIScrollView, scrollView == webView.scrollView else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } if let newSize = change?[.newKey] as? CGSize, !newSize.equalTo(lastContentSize) { lastContentSize = newSize print("ContentSize changed to (\(newSize.width), \(newSize.height))") // 这里添加你的业务逻辑 } }
额外说明
有没有办法让KVO只在值真正变化时触发?理论上可以通过重写被观察对象的automaticallyNotifiesObserversForKey:方法来控制,但WKWebView的scrollView是系统控件,我们无法修改它的实现,所以自行过滤是最稳妥的方案。
内容的提问来源于stack exchange,提问作者hidden-username
相关产品推荐
相关产品推荐

