最近優化一個tableView內嵌webView的文章模塊,需要準確算出網頁的高度來調整cell的高度,采用的優化方案是拿到HTML的字符串之后,用正則選出所有的img標簽的src屬性,并用本地的placeholder圖片地址替換掉再用webView加載,同時保存下來的src屬性用SDWebImage下載后,調用[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('img')[0].src = file:///..."];
來刷新圖片并重新計算webView高度。
前面的工作很順利,但是到了計算高度的時候發現高度計算有問題,替換src后計算出來的高度還是替換前的圖片高度,同樣的問題在StackOverFlow上也有人提問,iOS stringByEvaluatingJavaScriptFromString issue,做了不少測試之后發現雖然- stringByEvaluatingJavaScriptFromString:
會阻塞線程,但是更換圖片這個過程是異步的,也就是說計算高度的時候圖片并沒有完成替換。
聯想到web加載完成圖片有onload回調,于是決定嘗試JavaScript Core來實現oc -> js -> oc 的方案。
具體方案
在把src替換成本地地址時,加入onload回調
<img src = 'file:///......', onload='caculateHeight()'>
在處理API返回的HTML字符串時,在最前面加上以下js代碼
<script type="text/javascript">
function caculateHeight() {
var height = document.getElementById('webview_content_wrapper').clientHeight
finishLoad(height)
}
</script>
在- webViewDidFinishLoad:
中添加以下代碼
JSContext *ctx = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"finishLoad"] = ^() {
NSArray *args = [JSContext currentArguments];
for (JSValue *value in args) {
NSLog(@"%@", value.toString); // 這里就能拿到計算后的高度了
}
};
完成以上修改后就能準確計算出webView的高度了,流程是 js修改src -> 觸發onload() -> caculateHeight() -> finishLoad(),在修改src后和finishLoad后打印時間可以看到finishLoad延遲了0.006秒,這個時間差就造成了高度計算有誤,也驗證了上面提到的異步渲染圖片的機制
tips
1.一個bug,如果替換的字符串中回調的函數要帶參數進去,我們可能會這么寫
@"<img src = 'file:///......', onload='caculateHeight('args')'>"
但是這么寫不會調用caculateHeight (),需要改成下面這個形式
@"<img src = 'file:///......', onload=\"caculateHeight('args')\">"
2.JSContext的回調block,在iOS9和iOS10下都是默認子線程,更新UI需要回到主線程