产品中频繁遇到UITableViewCell含有WebView的需求,也提出好几个解决方案了,固然一次比一次简单。html
去年我总结出这个方案完美解决 UITableViewCell 中加载 UIWebView 的高度计算问题,个人思路是:web
tableView.reloadRows(at: [IndexPath (row: i, section: 0)], with: .none)
复制代码
若是相等,OK,第 i 个 cell 显示正确。bash
最近在作一个新的产品,又遇到了这个需求。原本个人想法是从上一个项目直接copy代码过来,可是看了半天以为太过繁琐,再加上最近看了一些UITableViewCell自适应高度的文章,就想换种写法。ui
通常状况下,实现UITableViewCell自适应高度这样作:lua
tableView.estimatedRowHeight = 76
tableView.rowHeight = UITableView.automaticDimension
复制代码
questionWebView.snp.makeConstraints { (make) in
make.edges.equalToSuperview().inset(UIEdgeInsets.init(top: 8, left: 16, bottom: 8, right: 16))
make.height.equalTo(60)
}
复制代码
对于通常的view,这两步以后就能够实现cell的自适应高度了。spa
可是对于webView,在开始加载时webView的高度并不固定,因此要在webView加载完毕后获取其高度并刷新cell。这一步就不用旧方案的step4来刷新cell了。调试
首先为webView监听webView加载:code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.separatorStyle = .none
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ExerciseQuestionCell
// 为cell设置数据
cell.setData(exerciseArray[indexPath.row])
// 监听webView加载
cell.questionWebView.delegate = self
cell.selectionStyle = .none
return cell
}
复制代码
获取webView的高度。cdn
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 当webview加载完成,计算并刷新高度
webView.evaluateJavaScript("document.body.scrollHeight") { (any, error) in
// height就是加载完毕的webView的高度
let height = any as! Int
}
}
复制代码
获取到高度后调整webView的高度:htm
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 当webview加载完成,计算并刷新高度
webView.evaluateJavaScript("document.body.scrollHeight") { (any, error) in
// height就是加载完毕的webView的高度
let height = any as! Int
// 调整webView高度
loadingWebView.snp.updateConstraints { (make) in
make.height.equalTo(height)
}
// 刷新tableView
exerciseTable.beginUpdates()
exerciseTable.endUpdates()
}
}
复制代码
到这里效果就出来了
可是在这里我发现当滑动table时模拟器会变得十分卡顿,调试发现ExerciseQuestionCell的setData(exercise)方法在一直被调用,setData方法以下:
func setData(_ exercise: Exercise) {
do {
let data = exercise.question.data(using: .utf8)
let questionObject = try JSON.init(data: data!)
let question = questionObject["question"].stringValue
let questionHtml = DIV_HEAD + question + DIV_FOOT
webView.loadHTMLString(htmlString, baseURL: nil)
} catch {
print(error)
}
}
复制代码
我想多是刷新tableView的时候调用setData方法,setData里面调用webView.loadHTMLString方法加载html,加载完毕后又刷新tableView......产生循环,因此我在ExerciseQuestionCell里面设置了一个变量loadedData来记录cell是否设置过数据,若是设置过数据就再也不设置了:
func setData(_ exercise: Exercise) {
if loadedData {
// 设置过数据以后就再也不设置,防止屡次刷新
return
}
do {
let data = exercise.question.data(using: .utf8)
let questionObject = try JSON.init(data: data!)
let question = questionObject["question"].stringValue
let questionHtml = DIV_HEAD + question + DIV_FOOT
questionWebView.loadHtmlString(questionHtml)
} catch {
print(error)
}
loadedData = true
}
复制代码
这样一来就很清爽了。