如何基于WKWebView内容高度实时更新UITableViewCell高度?
需要实现基于WKWebView内容高度动态更新UITableViewCell的高度。最初尝试用document.body.offsetHeight获取高度,但WebView末尾会出现空白,于是改用监听WKWebView导航完成事件,通过webView.scrollView.contentSize.height设置约束并刷新TableView:
extension PDDescriptionCell: WKNavigationDelegate { func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { self.summaryHeight?.constant = 0 self.contentView.layoutIfNeeded() } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.summaryHeight?.constant = webView.scrollView.contentSize.height if let tableView = self.superview as? UITableView { tableView.beginUpdates() tableView.endUpdates() } self.contentView.layoutIfNeeded() } }
但实际运行时,位于ProductDetail UITableViewController第3行的PDDescriptionCell,首次滚动到该行时高度还是xib的初始值,只有滚动回第1行再返回第3行,高度才会更新为正确值。希望沿用当前获取高度的逻辑,解决更新时机的问题。
PDDescriptionCell完整代码如下:
class PDDescriptionCell: UITableViewCell { var product: ProductModel? { didSet { if let product = product { print(product.summary) loadContent(product.summary) } } } IBOutlet weak var summaryView: WKWebView! IBOutlet weak var summaryHeight: NSLayoutConstraint! private let configuration = WKWebViewConfiguration() override func awakeFromNib() { super.awakeFromNib() print("awake from nib") contentView.backgroundColor = .background1 summaryView.navigationDelegate = self let preference = WKWebpagePreferences() preference.preferredContentMode = .mobile preference.allowsContentJavaScript = true let configuration = WKWebViewConfiguration() configuration.defaultWebpagePreferences = preference if let product = product { _ = WKWebViewConfiguration() print("HTML string is loaded") self.summaryView.loadHTMLString(product.summary, baseURL: nil) self.loadContent(product.summary) } } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } private func loadContent(_ content: String) { let styledHTML = """ <html> <head> <style> body { font-size: 30px; /* Change the font size for the body as needed */ font-family: -apple-system; /* Use the system font */ } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #dddddd; text-align: left; padding: 8px; font-size: 20px; /* Change the font size for table cells as needed */ } th { background-color: #f2f2f2; } img { width: 100%; /* Set the width of images to 100% */ height: auto; /* Set the height of images to auto to maintain aspect ratio */ margin-bottom: 50px; /* Add space below each image */ } .semibold { font-weight: 600; /* Set font weight to semibold */ } </style> <script type="text/javascript"> window.onload = function() { var body = document.body; var html = document.documentElement; var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); window.webkit.messageHandlers.iOS.postMessage(height); } </script> </head> <body> \(content) </body> </html> """ self.summaryView.loadHTMLString(styledHTML, baseURL: nil) } } extension PDDescriptionCell: WKNavigationDelegate { func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { self.summaryHeight?.constant = 0 self.contentView.layoutIfNeeded() } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.summaryHeight?.constant = webView.scrollView.contentSize.height if let tableView = self.superview as? UITableView { tableView.beginUpdates() tableView.endUpdates() } self.contentView.layoutIfNeeded() } }
也曾尝试用evaluateJavaScript("document.readyState")和evaluateJavaScript("document.body.scrollHeight")获取高度,但仍存在末尾空白问题,希望保留当前高度获取逻辑。
1. 修正awakeFromNib中的无效代码
原awakeFromNib存在两处无效逻辑:重新创建的WKWebViewConfiguration未被使用,且product在此时大概率未赋值,加载HTML的代码不会执行。修改后:
override func awakeFromNib() { super.awakeFromNib() print("awake from nib") contentView.backgroundColor = .background1 summaryView.navigationDelegate = self // 禁用WebView自身滚动,避免与TableView滚动冲突 summaryView.scrollView.isScrollEnabled = false }
2. 正确获取所在的UITableView
当前通过self.superview as? UITableView获取TableView的方式错误(Cell的直接父视图是内容容器,而非TableView),添加递归查找方法:
private func findTableView() -> UITableView? { var view = superview while view != nil { if let tableView = view as? UITableView { return tableView } view = view?.superview } return nil }
替换didFinish中的TableView获取逻辑,改用精准刷新当前行:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.summaryHeight?.constant = webView.scrollView.contentSize.height if let tableView = findTableView(), let indexPath = tableView.indexPath(for: self) { tableView.reloadRows(at: [indexPath], with: .automatic) } self.contentView.layoutIfNeeded() }
3. 确保TableView自动高度配置正确
在ProductDetail UITableViewController的viewDidLoad中添加自动行高配置:
override func viewDidLoad() { super.viewDidLoad() // 启用自动计算行高 tableView.rowHeight = UITableView.automaticDimension // 设置预估行高(尽量接近实际高度,减少滚动跳动) tableView.estimatedRowHeight = 400 // 根据xib初始值调整 }
4. 延迟获取高度(解决图片加载导致的偏差)
若HTML包含图片,didFinish触发时图片可能未加载完成,导致高度计算不准确,可添加短延迟:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self else { return } self.summaryHeight?.constant = webView.scrollView.contentSize.height if let tableView = self.findTableView(), let indexPath = tableView.indexPath(for: self) { tableView.reloadRows(at: [indexPath], with: .automatic) } self.contentView.layoutIfNeeded() } }
5. 清理冗余脚本
HTML中window.webkit.messageHandlers.iOS.postMessage(height)的脚本未被监听,属于冗余代码,可直接删除。
内容的提问来源于stack exchange,提问作者Shahnas Ettool

