如何使用底层WKWebKit自动调整UITableViewCell的大小,以便它尊重Web视图的内容?

在我的iOS应用程序中,我想显示包含一些HTML的几个项目。 为此,我构建了一个包含UITableViewNewsListViewController 。 对于那个UITableView (outlet: newsListTableView ),我创建了一个名为NewsTableViewCell的自定义UITableViewCell ,它将通过委托加载到它中。 NewsTableViewCell拥有一个WKWebKit控件,可以显示我的HTML。 这是我的代码:

NewsListViewController.swift

import UIKit class NewsListViewController: UIViewController { @IBOutlet weak var newsListTableView: UITableView! override func viewDidLoad() { super.viewDidLoad() self.newsListTableView.delegate = self self.newsListTableView.dataSource = self self.newsListTableView.register(UINib(nibName: "NewsTableViewCell", bundle: nil), forCellReuseIdentifier: "cell") } } extension NewsListViewController: UITableViewDelegate { } extension NewsListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 300.0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NewsTableViewCell cell.titleText = "My Title" cell.dateTimeText = "01.01.1998" cell.contentHtml = "html, body { margin: 0; padding: 0; font-size: 15px; }Hello News
Hello News
Hello News
Hello News" return cell } }

NewsTableViewCell.xib

NewsTableViewCell.xib预览

我的WKWebKit (outlet: newsContentPreviewWebView )位于UIView (outlet: newsContentViewContainer )内部,具有适当的约束以与UIView一起成长。 我的UIView也有适当的限制来与整个细胞一起成长。

NewsTableViewCell.swift

 import UIKit import WebKit @IBDesignable class NewsTableViewCell: UITableViewCell { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var dateTimeLabel: UILabel! @IBOutlet weak var newsContentViewContainer: UIView! @IBOutlet weak var newsContentPreviewWebView: WKWebView! @IBInspectable var titleText: String? { get { return self.titleLabel.text } set(value) { self.titleLabel.text = value } } @IBInspectable var dateTimeText: String? { get { return self.dateTimeLabel.text } set(value) { self.dateTimeLabel.text = value } } @IBInspectable var contentHtml: String? { get { return nil } set(value) { self.newsContentPreviewWebView.loadHTMLString(value ?? "", baseURL: nil) } } override func awakeFromNib() { super.awakeFromNib() self.newsContentPreviewWebView.navigationDelegate = self } } extension NewsTableViewCell: WKNavigationDelegate { } 

这是结果:

没有自动调整大小的应用预览

现在,我希望单元格尽可能小,但仍然显示完整的WKWebKit内容。

当我删除…

 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 300.0 } 

…来自NewsListViewController.swift ,单元格高度仅尊重WKWebKit控件上方的标签:

应用预览显示单元格自动调整大小失败

我认为这种情况正在发生,因为当应用程序调整单元格大小时,我的WKWebKit内容未加载。

我试着通过听WKWebKit的>>在我的NewsTableViewCell.swift中完成导航<<委托调用并调整父视图和整个单元格高度来解决这个问题:

 extension NewsTableViewCell: WKNavigationDelegate { func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in if complete != nil { webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in let h: CGFloat = height as! CGFloat let newsContentFrame: CGRect = self.newsContentViewContainer.frame self.newsContentViewContainer.frame = CGRect(x: newsContentFrame.minX, y: newsContentFrame.minY, width: newsContentFrame.width, height: h) let tableCellFrame: CGRect = self.frame self.frame = CGRect(x: tableCellFrame.minX, y: tableCellFrame.minY, width: tableCellFrame.width, height: tableCellFrame.height + h) }) } }) } } 

这是结果:

应用预览显示手动设置大小时单元格的重叠

细胞重叠,这表明我试图以错误的方式解决这个问题。

自动调整自定义表格单元格大小以适应其所有内容(包括WKWebKit的内容)的正确方法是什么?

问题是你永远不会给你的细胞一个高度。 手动设置框架

self.frame = CGRect(x:tableCellFrame.minX,y:tableCellFrame.minY,width:tableCellFrame.width,height:tableCellFrame.height + h)

不好。 您应始终使用专用委托方法或估计行高技术( 在UITableView中使用自动布局来获取动态单元格布局和可变行高 )。

问题很棘手:您需要等待webview加载以计算其高度。 但是在加载webview的内容之前调用heightForCell时需要它的高度。

我看到5个解决方案:

  • 您将tableview转换为包含所有新闻的巨型Web视图。 然后手动将元数据注入html
  • 您解析html并提取所需的信息。 只有当所有新闻html具有相同的结构时,才可能这样做。 这也很难( 除了XHTML自包含标签之外,RegEx匹配开放标签 )。
  • 你像你一样继续前进。 但是当调用completionHandler时,你会调用一个委托(例如你的viewcontroller),它会重新加载相应的单元格并给它正确计算的高度。 这个解决方案很简单,但在每次加载之前,您需要在您的单元格中给出错误的高度。 因此,您可以在单元格中定义加载状态,但是当单元格即将出现时,您将始终遇到一些小问题。
  • 在显示tableview之前,加载所有单元格的内容并在后台线程上计算它们的相应高度。 您可以为每个单元格使用一个Operation ,它将等待单元格的html加载并评估给定的javascript。 完成所有操作后,重新加载tableview
  • 更改设计:仅显示新闻列表,并在选择单元格时将新闻内容加载到单独的视图控制器中