如何获取WKWebView中静态页面完全渲染后的最终高度
你遇到的这个问题我之前也碰到过——didFinishNavigation:确实会在页面主文档加载完成后就触发,但此时页面里的图片、样式表等资源可能还在加载渲染,导致contentSize还没稳定下来。而KVO虽然能捕捉到contentSize的变化,但没法直接判断哪一次是最终值。既然你没法修改网页内容,那这几个纯原生的方案应该能帮到你:
方案一:利用CATransaction的Completion Block
UIKit的渲染操作基于CATransaction,我们可以在didFinishNavigation:里提交一个空事务,然后在它的completion block中获取contentSize。这个block会在当前所有待处理的UI渲染操作完成后执行,能有效避免提前获取高度的问题:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { CATransaction.begin() CATransaction.setCompletionBlock { let finalHeight = webView.scrollView.contentSize.height print("Finished rendering, final height: \(finalHeight)") // 在这里处理你的业务逻辑 } CATransaction.commit() }
方案二:KVO + 防抖定时器
如果页面存在异步加载内容(比如懒加载图片),CATransaction的方案可能不够稳定。这时可以结合KVO和防抖逻辑:每次contentSize变化时重置定时器,只有当一段时间内(比如100ms)没有再变化时,才认为这是最终高度。
首先在ViewController中添加属性:
var myContext = 0 var contentSizeObservationTimer: Timer? var lastRecordedHeight: CGFloat = 0
然后在viewDidLoad中添加观察者:
override func viewDidLoad() { super.viewDidLoad() webView.navigationDelegate = self webView.uiDelegate = self webView.scrollView.addObserver(self, forKeyPath: "contentSize", options: .new, context: &myContext) webView.load(URLRequest(url: URL(string: "https://www.rockade.nl/testpage/testpage.php")!)) }
实现观察者方法和防抖逻辑:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard context == &myContext, keyPath == "contentSize" else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } let currentHeight = webView.scrollView.contentSize.height // 高度变化极小,判定为趋于稳定 if abs(currentHeight - lastRecordedHeight) < 1 { contentSizeObservationTimer?.invalidate() contentSizeObservationTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { [weak self] _ in guard let self = self else { return } print("Final stable height: \(currentHeight)") // 处理业务逻辑后记得移除观察者,避免内存泄漏 self.webView.scrollView.removeObserver(self, forKeyPath: "contentSize", context: &self.myContext) } } else { lastRecordedHeight = currentHeight contentSizeObservationTimer?.invalidate() contentSizeObservationTimer = nil } }
记得在ViewController销毁前清理资源:
deinit { contentSizeObservationTimer?.invalidate() webView.scrollView.removeObserver(self, forKeyPath: "contentSize", context: &myContext) }
方案三:监听WKWebView的estimatedProgress
WKWebView的estimatedProgress属性会从0.0到1.0表示页面加载进度,当它接近1.0时,说明所有资源基本加载完成。我们可以结合这个属性和CATransaction来获取高度:
先添加观察者:
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: &myContext)
实现观察者方法:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard context == &myContext else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } if keyPath == "estimatedProgress", webView.estimatedProgress >= 0.999 { CATransaction.begin() CATransaction.setCompletionBlock { let finalHeight = self.webView.scrollView.contentSize.height print("Progress complete, final height: \(finalHeight)") self.webView.removeObserver(self, forKeyPath: "estimatedProgress", context: &self.myContext) } CATransaction.commit() } }
这个方案适合资源较多的页面,但如果网页存在动态加载的内容,还是需要结合防抖逻辑来确保高度稳定。
内容的提问来源于stack exchange,提问作者Roel Koops

