問題產生背景:
新開發的頁面中有一部分的界面是需要展示后端接口返回的HTML代碼,包括文字和圖片。所以就自然而然的要使用iOS原生的WebKit. 鑒於Xcode 8發布以后,編譯器支持的最低版本(Deployment Target)也變為iOS8。因此放棄了UIWebView, 直接使用WKWebView(何況蘋果宣稱WKWebView的性能相比UIWebView有了極大的提升)。
坑一:獲取不到WKWebView的高度
獲取方法:在WKWebView加載成功的代理方法里獲取WKWebView的UIScrollView的contentSize
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { self.webViewContentHeight = self.webView.scrollView.contentSize.height; }
運行后,發現獲取不到contentSize, 打印結果顯示(width = 0, height =0).
解決辦法:使用KVO監聽WKWebView的contentSize
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (!self.webView.isLoading) { if([keyPath isEqualToString:@"scrollView.contentSize"]) { self.webViewContentHeight = self.webView.scrollView.contentSize.height; CGRect frame = self.webView.frame; frame.size.height = self.webViewContentHeight; self.webView.frame = frame; [self.webView sizeToFit]; } } }
Bingo! 完美取到WKWebView的height.
坑二:移除KVO的keypath時,程序crash。
- (void)dealloc { [self.webView removeObserver:self forKeyPath:@"scrollView.contentSize" context:nil]; }
這種情況通常出現在對同一個keypath進行兩次remove,如父類中有一個kvo, 父類在dealloc的時候remove一次,子類dealloc的時候又remove一詞。看到這里想必大家都已經知道解決思路了吧?那就是區分父類和子類的KVO,回頭看一下發現addObserver和removeObserver中都有一個context參數,沒錯,這個參數就可以用來標記我們自己添加的KVO。
[self.webView addObserver:self forKeyPath:@"scrollView.contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"DJWebKitContext"];
- (void)dealloc { [self.webView removeObserver:self forKeyPath:@"scrollView.contentSize" context:@"DJWebKitContext"]; }
運行一下,退出頁面的時候果然不會crash了😊
坑三: 頁面刷新時crash,代碼如下:
- (void)refreshView { [self constructWebView]; } - (void)constructWebView { _webView = [[WKWebView alloc] initWithFrame:CGRectMake(5, 64+10, WindowWidth-10, 150)]; self.webView.backgroundColor = [UIColor clearColor]; self.webView.layer.borderWidth = 0.5; self.webView.layer.borderColor = [UIColor grayColor].CGColor; self.webView.scrollView.scrollEnabled = YES; self.webView.scrollView.directionalLockEnabled = NO; self.webView.scrollView.scrollsToTop = NO; self.webView.scrollView.userInteractionEnabled = YES; self.webView.navigationDelegate = self; [self.webView loadHTMLString:@"" baseURL:nil]; [self.webView addObserver:self forKeyPath:@"scrollView.contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"DJWebKitContext"]; [self.view addSubview:self.webView]; }
這個問題是由於在refreshView的時候webView屬性被重新分配了對象,那么舊的對象就會被釋放掉,但是在這個過程中並沒有將舊對象的KVO remove掉,所以就會crash。解決辦法就是在對webView分配新對象前先 remove observer,或者如果是webView不需要新對象的話,可以判斷如果webView存在,就不重新初始化:
- (void)refreshView { [self constructWebView]; } - (void)constructWebView { if (_webView) { [self.webView removeObserver:self forKeyPath:@"scrollView.contentSize" context:@"DJWebKitContext"]; } _webView = [[WKWebView alloc] initWithFrame:CGRectMake(5, 64+10, WindowWidth-10, 150)]; self.webView.backgroundColor = [UIColor clearColor]; self.webView.layer.borderWidth = 0.5; self.webView.layer.borderColor = [UIColor grayColor].CGColor; self.webView.scrollView.scrollEnabled = YES; self.webView.scrollView.directionalLockEnabled = NO; self.webView.scrollView.scrollsToTop = NO; self.webView.scrollView.userInteractionEnabled = YES; self.webView.navigationDelegate = self; [self.webView loadHTMLString:@"" baseURL:nil]; [self.webView addObserver:self forKeyPath:@"scrollView.contentSize" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"DJWebKitContext"]; [self.view addSubview:self.webView]; }
有興趣的同學可以下載demo來驗證這三個問題。同時,如果大家在使用過程中遇到過其它坑也歡迎告訴我,我會幫忙記錄下來供其他人參考。
