UITableViewCell含有WebView的自適應高度新解決方案

產品中頻繁遇到UITableViewCell含有WebView的需求,也提出好幾個解決方案了,固然一次比一次簡單。html

舊方案

去年我總結出這個方案完美解決 UITableViewCell 中加載 UIWebView 的高度計算問題,個人思路是:web

  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 顯示正確。bash

  1. 重複 3。

新方案

最近在作一個新的產品,又遇到了這個需求。原本個人想法是從上一個項目直接copy代碼過來,可是看了半天以爲太過繁瑣,再加上最近看了一些UITableViewCell自適應高度的文章,就想換種寫法。ui

通常狀況下,實現UITableViewCell自適應高度這樣作:lua

  1. 設置UITableView自適應cell高度
tableView.estimatedRowHeight = 76
    tableView.rowHeight = UITableView.automaticDimension
複製代碼
  1. UITableViewCell設置從top到bottom完整的約束
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
    }
複製代碼

這樣一來就很清爽了。

相關文章
相關標籤/搜索