WKWebView刷新机制小探

create at 2016.11.07 20:58

背景

iOS的一个坑。在线上的版本中,iOS10系统中,app内使用WKWebView当作一个普通的子View来展示一个较长的Web内容组成一个hybrid页面时,会发生白屏的。经过原生端的开发的排除,确认是WKWebView的机制问题,并不是页面加载不完整或者是被劫持而导致的问题。

为了更严谨的排出问题所在,我拉去了原声端的代码再次确认代码逻辑是否存在导致该问题所在的bug。因为该页面是一个自定义的UITableView,WKWebView只是UITableView的一个Cell里面的子View,而且和UITableView的model层,也有很多的业务逻辑,看起来比较费劲。经过了几轮的调试,知识找到了一个导致导致死循环的一个调用,那边的开发使用了RAC绑定WKWebView内嵌UIScrollView的contentSize,去刷新UITableView,UITableView的回调获取cell的高度的时候会导致循环调用,一直的刷新UITableView获取cell的高度,除了会消耗性能,并没有看出逻辑有太大问题。因为,目前并不会导致页面出现一些莫名其妙的问题,也不知道原来写这部分代码逻辑的同事初衷是什么,所以并没有改动这部分代码。另外,用了Charles看了一下这个页面的请求,并不是页面劫持导致的问题。

  • 不是请求劫持导致的问题
  • http请求完整
  • 问题必现,证明是通用性问题

尝试设置WKWebView的frame比contentSize小,在滚动WKWebView的时候,里面的内容是可以全部展示的,并没有出现白屏的问题。可以得出的结论是:WKWebView作为一个元素放在UITabViewCell里面,是没问题问题的(当然,性能问题在讨论范围)。

调试了大半天,并没有找到问题的根源。于是先建立一个demo工程,先确认和排出一些问题。

  • UITableViewCell中嵌套WKWebView是否会导致刷新问题
  • UITabView中计算获取嵌套了WKWebView的UITabViewCell计算高度是否准确

建立工程,在UITableVie的一个UITableViewCell里面嵌套了一个WKWebView来重现工程中的情况。

Reveal

先通过Reveal工具来看一下WKWebView的树,先大概了解一些WKWebView的结构。

WKScrollView

WKScrollView继承于UIScrollView,在初始化的时将初始化一个WKScrollViewDegelageForwarder代理实例

下图是WKScrollView的delegate的setter方法,可以清晰的看到各个delegate的类型

在WKScrollViewDegelageForwarder的实现中,明确的看到,WKScrollView的delegate(externalDelegate实例)的消息都通过message_forward的形式转发到WKWebView(internalDelegate)实例中。

下图是WKScrollViewDegelageForwarder类的转发实现

WKContentView

WKContentView就是WKWebView内容渲染的容器。在Reveal的树状图上面可以看到,渲染页面中,展示在页面上的渲染单元是WKCompositingView,WKCompositingView可以嵌套WKCompositingView。其中的一个WKCompositingView实例,将包含多个WKCompositingView子实例。类似于UITableView的重用机制,多个WKCompositingView的父View就相当于UITableView,WKCompositingView就相当于UITableViewCell,只展示可视区域的内容,达到性能优化的目的。

从下图可以看到,一个WKWebView加载的web内容,切割成多个WKCompositingView,单个WKCompositingView重用单元的面积是375x512点。

WKWebView

初始化

在WKWebView初始化的代码中,可以看到这样的一段初始化代码

init

ScrollView回调

在WKWebView中,ScrollView相关的回调的调用链都是这样的一个调用关系:

scrollview delegate's callback 
->[WKWebView _updateVisibleContentRectAfterScrollInView:]
->[WKWebView _updateContentRectsWithState:]
->[WKContentView didUpdateVisibleRect:visibleRectInContentCoordinates
                       unobscuredRect:unobscuredRectInContentCoordinates
unobscuredRectInScrollViewCoordinates:unobscuredRect
                        obscuredInset:CGSizeMake(_obscuredInsets.left, _obscuredInsets.top)
                                scale:scaleFactor 
                         minimumScale:[_scrollView minimumZoomScale]
                        inStableState:inStableState
 isChangingObscuredInsetsInteractively:_isChangingObscuredInsetsInteractively
      enclosedInScrollableAncestorView:scrollViewCanScroll([self _scroller])];

从调用链上清晰可以看到,当WKScrollView滚动的时候,WKScrollView滚动相关回调的消息,将会发送到WKWebView内,WKWebView实例内scrollView的的回调将会调用WKContntView的刷新方法,刷新需要渲染的web内容。

猜想

当了解到WKWebView内容的刷新机制以后,就可以合理的进行猜想了。
因为WKWebView作为一个普通的UIView添加在UITableViewCell的contentView上,因为项目中UITableView和WKWebView的ScrollView都是竖向滚动的,这两个手势动作将会冲突,WKWebView只是一个子View,需要通过设置内置ScrollView的滚动属性来将WKWebView的滚动功能关闭,保证父View--UITableView滚动功能的正常使用。

因为WKWebView使用过绑定内置ScrollView的滚动回调来刷新WKContentView内需要渲染的web内容的,因为WKWebView已经被设定为禁止滚动,自然不会再刷新需要渲染当初在不在可视区域的内容了。因为UITableView的滚动回调并没有和WKWebView的内的滚动是绑定关系,所以在UITableView滚动的时候,并不会触发WKWebView的刷新。这就是为什么在进入页面的时候,上面一部分内容可以正常显示,二下半部分显示白屏的原因。当然,在目前来说只是一个猜想。

验证

前面的猜想,在经过对源代码的阅读,理论上是说得通的。现在就通过demo的代码验证。有了上面的原理,那么UITablbeView滚动的时候,触发WKWebView刷新页面即可?可知的是,WKWebView是调用_updateVisibleContentRectAfterScrollInView:方法来对WKContentView来刷新内容的。

由下图可知,而WKWebView的_updateVisibleContentRects方法实现,也只是调用了_updateVisibleContentRectAfterScrollInView:,也就是说直接调用WKWebView实例的_updateVisibleContentRects就可以刷新了。

下面,用RAC来监听一下UITableView实例的contentOffset属性,在contentOffset发生变化的时候,也就是UITableView实例滚动的时候,就去调用一下WKWebView实例的_updateVisibleContentRects方法去刷新需要渲染的内容。

    @weakify(self);
    [RACObserve(self.tableView, contentOffset) subscribeNext:^(id x) {
        @strongify(self);
        if ([self.webView respondsToSelector:@selector(_updateVisibleContentRects)]) {
            ((void(*)(id,SEL,BOOL))objc_msgSend)(self.webView,@selector(_updateVisibleContentRects),NO);
        }
    }];

在运行demo工程的时候,结果按照猜想的发生了,滚动到WKWebView下方时,原来会白屏的区域正常的渲染内容了。

解决方案

上方猜想被证实了,那么说这个方案时可以行的,而且对源代码的理解并没有太大的偏差。按照原理,可以使用一下几个方法来解决白屏的问题

  • 用KVO方法监听UITableView的contnetOffset属性,contentOffset发生变化也就是说UITableView发生滚动,调用WKWebView实例的_updateVisibleContentRects,刷新需要渲染的内容
  • UITableView是继承自UIScrollView的,在代码中实现UIScrollView的delegate,在delegate实现中手动调用WKWebView实例等UIScrollViewDelegate的方法,原理和第一种方法一样
  • 使用CADisplayLink类,在CADisplayLink的回调方法里面调用WKWebView实例的_updateVisibleContentRects即可

上面三种方法的其实都是大同小异的,只是适合不同的场景。优劣也不用说了,一眼就能看出来了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容