项目中遇到在 UITableViewCell 中加载 WKWebView 的需求,具体说就是要在一个 TableView 中显示若干个 WebView,每个WebView 的高度不尽相同。这个问题一直困扰了大半年,今天终于完美的解决了。
我的思路是:
1 获取数据,确定 tableView 的 cell 的数目和初始高度。
2 刷新 tableView,向每一个 cell 填充内容和初始高度,将初始高度赋值给 cell 的一个变量 cellHeight,并加载 webView。
3 等第 i 个 cell 中的 webView 加载完毕之后计算其高度 h。
4 令 h 与 cellHeight 进行比较。如果两个高度不等,通知 tableView 第 i 个 cell 的高度 h (即 cell 中 webView的实际高度),并调用如下代码刷新 cell:
tableView.reloadRows(at: [IndexPath (row: i, section: 0)], with: .none)
如果相等,OK,第 i 个 cell 显示正确。
5 重复 3
从得到数据开始说起,比如有16条数据需要显示:
for _ in 0 ... 16 {
let htmlString = "......"
htmlStringArray.append(htmlString)
cellHeightArray.append(60)
}
tableView.reloadData()
我在 TableView 中加载的数据放在数组 titleArray<String> 里面,数组中每一项是需要显示的内容,一个 html 字符串。TableView 的每一个 cell 的高度我放在另一个数组 titleCellHeightArray<Float> 里面。那么 TableView 中第 i 个 cell 的高度即 titleCellHeightArray[i]。所以我们可以通过这两个数组确定 UITableView 的 cell 的数目、高度和内容:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return htmlStringArray.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(cellHeightArray[indexPath.row])
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! WKWebViewCell
cell.delegate = self
cell.tag = indexPath.row
cell.setContent(htmlStringArray[indexPath.row], andHeight: cellHeightArray[indexPath.row])
return cell
}
UITableViewCell如下,布局中就一个 UIWebView,
let HTML_HEAD = "<!DOCTYPE HTML><html><head><title>test</title><meta charset='utf-8'/><meta name='viewport' content='width=device-width, initial-scale=1'/><style>body{margin:0px;border:0px}</style></head><body id='test-body'>"
let HTML_FOOT = "</body></html>"
let IMAGE = "<img src='https://gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg'>"
var delegate:WebViewCellHeightDelegate?
let webView = WKWebView()
var cellHeight = 0
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
initView()
}
func initView() {
contentView.addSubview(webView)
webView.navigationDelegate = self
webView.isOpaque = false
webView.scrollView.bounces = false
webView.backgroundColor = UIColor.clear
webView.snp.makeConstraints { (make) in
make.edges.equalToSuperview().inset(UIEdgeInsetsMake(10, 10, 10, 10))
}
}
func setContent(_ content: String, andHeight height: Int) {
cellHeight = height
webView.loadHTMLString(HTML_HEAD + content + IMAGE + HTML_FOOT, baseURL: nil)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.body.scrollHeight") { (result, error) in
let h = (result as! Int) + 20
//高度不同才通知,相同说明显示正确,不通知
if (h != self.cellHeight) {
self.delegate?.cell(height: h, withTag: self.tag)
}
}
}
/// 将 cell 的高度通知给 tableView
///
/// - Parameters:
/// - height: cell 的高度
/// - tag: cell 在 tableView 的位置
protocol WebViewCellHeightDelegate:class {
func cell(height: Int, withTag tag: Int)
}
在 ViewController 中实现代理方法
func cell(height: Int, withTag tag: Int) {
cellHeightArray[tag] = height
tableView.reloadRows(at: [IndexPath (row: tag, section: 0)], with: .none)
}
至此,问题解决。效果如下