问题产生背景:
新开发的页面中有一部分的界面是需要展示后端接口返回的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来验证这三个问题。同时,如果大家在使用过程中遇到过其它坑也欢迎告诉我,我会帮忙记录下来供其他人参考。