WKWebView使用过程中的那些坑

问题产生背景:  

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

posted @ 2016-10-12 15:07  EllaDu  阅读(6176)  评论(0编辑  收藏  举报