WKWebView通過監(jiān)聽ContentSize更新WebView的Frame導(dǎo)致Height持續(xù)增長的bug解決方案(編輯于20180813)

問題描述

最近眼紅WKWebView相對UIWebView的高可配置和高效。在最近的代碼中選擇使用WKWebView來加載JS動態(tài)頁面。但是需要把WebView嵌入到另一個ScrollView中顯示,所以需要拉伸到內(nèi)容長度且不能被用戶拖動。原始代碼如下:

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(NSNumber *height, NSError * _Nullable error) {
        float htmlHeight =[height floatValue];
        CGRect webFrame = webView.frame;
        webFrame.size.height = htmlHeight;
        webView.frame = webFrame;
        [self addFeedBackBtn];
    }];
}

通過JS代碼得到頁面的高度。
但是這樣的方法對于非靜態(tài)頁面無效,高度并非正確的高度。

解決辦法


編輯于20180813
下面的分割線后是最初始的解決辦法,但是在部分極端情況下還有用戶反應(yīng)會出現(xiàn)高度持續(xù)增長的問題。
最終找到的原因WKWebViewframe寬度帶有小數(shù),而不是整數(shù)。猜測WKWebView在計算contentSize的時候是依照frame.width向下取整的寬度來計算最終的contentSizeheight的,然后導(dǎo)致KVO觀察的contentSize高度在更新frame高度后持續(xù)增加。
更新的解決辦法是對WKWebView的frame寬度求整。

代碼如下_webView.frame = CGRectMake(HWFrame_IPhonePadX(20, 30), height, (NSInteger)(ScreenWidth - HWFrame_IPhonePadX(60, 80)), 0);。

BTW. 之所以我們的WKWebViewframe寬度會帶小數(shù)是因為我們的HWFrame_IPhonePadX宏會自適應(yīng)屏幕寬高生成輸入數(shù)值對應(yīng)的實際顯示數(shù)值(這個數(shù)字是比例計算而來,會帶有小數(shù)位)。所以就尷尬了,產(chǎn)生了WKWebView高度持續(xù)增加的bug。


初始解決方案
為了監(jiān)控JS動態(tài)頁面的當(dāng)前高度,選擇使用KVO監(jiān)聽WKWebView.scrollView的contentSize變化。在收到變化通知是,通過當(dāng)前的contentSize更新webView的frame。代碼如下:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (object == _webView.scrollView && [keyPath isEqualToString:@"contentSize"]) {
        NSLog(@"_webView.scrollView.contentSize %f %f", self.webView.scrollView.contentSize.width, self.webView.scrollView.contentSize.height);
        CGRect webFrame = self.webView.frame;
        webFrame.size.height = self.webView.scrollView.contentSize.height;
        self.webView.frame = webFrame;
    }
}

該方法可以基本解決訪問動態(tài)頁面,實時更新頁面高度的問題。

使用KVO記得移除Observer,同時避免過度移除的crash。可以使用下方代碼解決

- (void)removeWebViewObserver {
    @try {
        if (_webView) {
            [_webView.scrollView removeObserver:self forKeyPath:@"contentSize"];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"多次刪除kvo 報錯了");
    }
}

偶發(fā)問題

上方方法小概率下會出現(xiàn)監(jiān)聽持續(xù)被觸發(fā),contentSize.height持續(xù)不斷增加,從而使得設(shè)置的frame的高度也在不斷增加,無限循環(huán)增高。
我當(dāng)時工程內(nèi)的表現(xiàn)為高度每次加一。

解決方案一

通過

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (object == _webView.scrollView && [keyPath isEqualToString:@"contentSize"]) {
        NSLog(@"_webView.scrollView.contentSize %f %f", self.webView.scrollView.contentSize.width, self.webView.scrollView.contentSize.height);
        CGRect webFrame = self.webView.frame;
        webFrame.size.height = self.webView.scrollView.contentSize.height - 1;    // 每次減一可以結(jié)局死循環(huán),持續(xù)增加高度。
        self.webView.frame = webFrame;
    }
}

該辦法可以解決持續(xù)增加高度的問題。但是沒有找到根本原因,無法確定所有頁面都是每次加一,還是有其他可能的情況。所以該方法放棄。

解決方案二

把WKWebView更換為UIWebView,同樣也通過監(jiān)聽contentSize來動態(tài)更新webView高度,沒有出現(xiàn)上方的偶發(fā)問題,高度不會持續(xù)增加。
但是WKWebView比UIWebView更加高效和蘋果更加推薦,所以也放棄。

解決方案三

測試代碼,發(fā)現(xiàn)如果不僅僅更新frame的height,取而代之的通過contentSize同時更新webView的frame的寬高,就不會出現(xiàn)高度的持續(xù)增高。但是頁面內(nèi)容寬度會失去限制,超過webView頁面范圍。

最終解決方案是通過JS代碼限制webView的內(nèi)容寬度。
方案是不更改監(jiān)聽代碼,也不對監(jiān)聽得到的高度減一。更新WKNavigationDelegate的- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation如下:

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    if (webView == _webView) {
        NSString *meta = [NSString stringWithFormat:@"document.getElementsByName(\"viewport\")[0].content = \"width=self.webView.frame.size.width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\""];
        [_webView evaluateJavaScript:meta completionHandler:^(id _Nullable complete, NSError * _Nullable error) {
            if (complete != nil) {
                NSLog(@"complete %@", complete);
            } else if (error) {
                NSLog(@"error %@", error);
            }
        }];
    }
}

通過強制設(shè)置頁面的寬度為我們當(dāng)前的webView的寬度。解決偶爾出現(xiàn)的高度持續(xù)增加的Bug。

Reference

How to determine the content size of a UIWebView?
iOS 修改webView字體大小,設(shè)置寬度及縮放效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容