最近優(yōu)化一個tableView內(nèi)嵌webView的文章模塊,需要準(zhǔn)確算出網(wǎng)頁的高度來調(diào)整cell的高度,采用的優(yōu)化方案是拿到HTML的字符串之后坝初,用正則選出所有的img標(biāo)簽的src屬性,并用本地的placeholder圖片地址替換掉再用webView加載,同時保存下來的src屬性用SDWebImage下載后疾呻,調(diào)用[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('img')[0].src = file:///..."];
來刷新圖片并重新計(jì)算webView高度。
前面的工作很順利写半,但是到了計(jì)算高度的時候發(fā)現(xiàn)高度計(jì)算有問題岸蜗,替換src后計(jì)算出來的高度還是替換前的圖片高度,同樣的問題在StackOverFlow上也有人提問叠蝇,iOS stringByEvaluatingJavaScriptFromString issue璃岳,做了不少測試之后發(fā)現(xiàn)雖然- stringByEvaluatingJavaScriptFromString:
會阻塞線程,但是更換圖片這個過程是異步的悔捶,也就是說計(jì)算高度的時候圖片并沒有完成替換铃慷。
聯(lián)想到web加載完成圖片有onload回調(diào),于是決定嘗試JavaScript Core來實(shí)現(xiàn)oc -> js -> oc 的方案炎功。
具體方案
在把src替換成本地地址時枚冗,加入onload回調(diào)
<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); // 這里就能拿到計(jì)算后的高度了
}
};
完成以上修改后就能準(zhǔn)確計(jì)算出webView的高度了蛇损,流程是 js修改src -> 觸發(fā)onload() -> caculateHeight() -> finishLoad()赁温,在修改src后和finishLoad后打印時間可以看到finishLoad延遲了0.006秒,這個時間差就造成了高度計(jì)算有誤淤齐,也驗(yàn)證了上面提到的異步渲染圖片的機(jī)制
tips
1.一個bug股囊,如果替換的字符串中回調(diào)的函數(shù)要帶參數(shù)進(jìn)去,我們可能會這么寫
@"<img src = 'file:///......', onload='caculateHeight('args')'>"
但是這么寫不會調(diào)用caculateHeight ()更啄,需要改成下面這個形式
@"<img src = 'file:///......', onload=\"caculateHeight('args')\">"
2.JSContext的回調(diào)block稚疹,在iOS9和iOS10下都是默認(rèn)子線程,更新UI需要回到主線程