You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取WKWebView中静态页面完全渲染后的最终高度

准确获取WKWebView完全渲染后的contentSize方案

你遇到的这个问题我之前也碰到过——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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:32