WKWebView刷新機(jī)制小探

create at 2016.11.07 20:58

背景

iOS的一個(gè)坑青扔。在線上的版本中锯七,iOS10系統(tǒng)中链快,app內(nèi)使用WKWebView當(dāng)作一個(gè)普通的子View來展示一個(gè)較長(zhǎng)的Web內(nèi)容組成一個(gè)hybrid頁面時(shí),會(huì)發(fā)生白屏的眉尸。經(jīng)過原生端的開發(fā)的排除域蜗,確認(rèn)是WKWebView的機(jī)制問題,并不是頁面加載不完整或者是被劫持而導(dǎo)致的問題噪猾。

為了更嚴(yán)謹(jǐn)?shù)呐懦鰡栴}所在霉祸,我拉去了原聲端的代碼再次確認(rèn)代碼邏輯是否存在導(dǎo)致該問題所在的bug。因?yàn)樵擁撁媸且粋€(gè)自定義的UITableView袱蜡,WKWebView只是UITableView的一個(gè)Cell里面的子View丝蹭,而且和UITableView的model層,也有很多的業(yè)務(wù)邏輯坪蚁,看起來比較費(fèi)勁奔穿。經(jīng)過了幾輪的調(diào)試,知識(shí)找到了一個(gè)導(dǎo)致導(dǎo)致死循環(huán)的一個(gè)調(diào)用敏晤,那邊的開發(fā)使用了RAC綁定WKWebView內(nèi)嵌UIScrollView的contentSize巫橄,去刷新UITableView,UITableView的回調(diào)獲取cell的高度的時(shí)候會(huì)導(dǎo)致循環(huán)調(diào)用茵典,一直的刷新UITableView獲取cell的高度湘换,除了會(huì)消耗性能,并沒有看出邏輯有太大問題。因?yàn)椴室校壳安⒉粫?huì)導(dǎo)致頁面出現(xiàn)一些莫名其妙的問題筹我,也不知道原來寫這部分代碼邏輯的同事初衷是什么,所以并沒有改動(dòng)這部分代碼帆离。另外蔬蕊,用了Charles看了一下這個(gè)頁面的請(qǐng)求,并不是頁面劫持導(dǎo)致的問題哥谷。

  • 不是請(qǐng)求劫持導(dǎo)致的問題
  • http請(qǐng)求完整
  • 問題必現(xiàn)岸夯,證明是通用性問題

嘗試設(shè)置WKWebView的frame比contentSize小,在滾動(dòng)WKWebView的時(shí)候们妥,里面的內(nèi)容是可以全部展示的猜扮,并沒有出現(xiàn)白屏的問題〖嗌簦可以得出的結(jié)論是:WKWebView作為一個(gè)元素放在UITabViewCell里面旅赢,是沒問題問題的(當(dāng)然,性能問題在討論范圍)惑惶。

調(diào)試了大半天煮盼,并沒有找到問題的根源。于是先建立一個(gè)demo工程带污,先確認(rèn)和排出一些問題僵控。

  • UITableViewCell中嵌套WKWebView是否會(huì)導(dǎo)致刷新問題
  • UITabView中計(jì)算獲取嵌套了WKWebView的UITabViewCell計(jì)算高度是否準(zhǔn)確

建立工程,在UITableVie的一個(gè)UITableViewCell里面嵌套了一個(gè)WKWebView來重現(xiàn)工程中的情況鱼冀。

Reveal

先通過Reveal工具來看一下WKWebView的樹喉祭,先大概了解一些WKWebView的結(jié)構(gòu)。

WKScrollView

WKScrollView繼承于UIScrollView雷绢,在初始化的時(shí)將初始化一個(gè)WKScrollViewDegelageForwarder代理實(shí)例

下圖是WKScrollView的delegate的setter方法泛烙,可以清晰的看到各個(gè)delegate的類型

在WKScrollViewDegelageForwarder的實(shí)現(xiàn)中,明確的看到翘紊,WKScrollView的delegate(externalDelegate實(shí)例)的消息都通過message_forward的形式轉(zhuǎn)發(fā)到WKWebView(internalDelegate)實(shí)例中蔽氨。

下圖是WKScrollViewDegelageForwarder類的轉(zhuǎn)發(fā)實(shí)現(xiàn)

WKContentView

WKContentView就是WKWebView內(nèi)容渲染的容器。在Reveal的樹狀圖上面可以看到帆疟,渲染頁面中鹉究,展示在頁面上的渲染單元是WKCompositingView,WKCompositingView可以嵌套WKCompositingView踪宠。其中的一個(gè)WKCompositingView實(shí)例自赔,將包含多個(gè)WKCompositingView子實(shí)例。類似于UITableView的重用機(jī)制柳琢,多個(gè)WKCompositingView的父View就相當(dāng)于UITableView绍妨,WKCompositingView就相當(dāng)于UITableViewCell润脸,只展示可視區(qū)域的內(nèi)容,達(dá)到性能優(yōu)化的目的他去。

從下圖可以看到毙驯,一個(gè)WKWebView加載的web內(nèi)容,切割成多個(gè)WKCompositingView灾测,單個(gè)WKCompositingView重用單元的面積是375x512點(diǎn)爆价。

WKWebView

初始化

在WKWebView初始化的代碼中,可以看到這樣的一段初始化代碼

init

ScrollView回調(diào)

在WKWebView中媳搪,ScrollView相關(guān)的回調(diào)的調(diào)用鏈都是這樣的一個(gè)調(diào)用關(guān)系:

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])];

從調(diào)用鏈上清晰可以看到铭段,當(dāng)WKScrollView滾動(dòng)的時(shí)候,WKScrollView滾動(dòng)相關(guān)回調(diào)的消息秦爆,將會(huì)發(fā)送到WKWebView內(nèi)序愚,WKWebView實(shí)例內(nèi)scrollView的的回調(diào)將會(huì)調(diào)用WKContntView的刷新方法,刷新需要渲染的web內(nèi)容鲜结。

猜想

當(dāng)了解到WKWebView內(nèi)容的刷新機(jī)制以后,就可以合理的進(jìn)行猜想了活逆。
因?yàn)閃KWebView作為一個(gè)普通的UIView添加在UITableViewCell的contentView上精刷,因?yàn)轫?xiàng)目中UITableView和WKWebView的ScrollView都是豎向滾動(dòng)的,這兩個(gè)手勢(shì)動(dòng)作將會(huì)沖突蔗候,WKWebView只是一個(gè)子View怒允,需要通過設(shè)置內(nèi)置ScrollView的滾動(dòng)屬性來將WKWebView的滾動(dòng)功能關(guān)閉,保證父View--UITableView滾動(dòng)功能的正常使用锈遥。

因?yàn)閃KWebView使用過綁定內(nèi)置ScrollView的滾動(dòng)回調(diào)來刷新WKContentView內(nèi)需要渲染的web內(nèi)容的纫事,因?yàn)閃KWebView已經(jīng)被設(shè)定為禁止?jié)L動(dòng),自然不會(huì)再刷新需要渲染當(dāng)初在不在可視區(qū)域的內(nèi)容了所灸。因?yàn)閁ITableView的滾動(dòng)回調(diào)并沒有和WKWebView的內(nèi)的滾動(dòng)是綁定關(guān)系丽惶,所以在UITableView滾動(dòng)的時(shí)候,并不會(huì)觸發(fā)WKWebView的刷新爬立。這就是為什么在進(jìn)入頁面的時(shí)候钾唬,上面一部分內(nèi)容可以正常顯示,二下半部分顯示白屏的原因侠驯。當(dāng)然抡秆,在目前來說只是一個(gè)猜想。

驗(yàn)證

前面的猜想吟策,在經(jīng)過對(duì)源代碼的閱讀儒士,理論上是說得通的。現(xiàn)在就通過demo的代碼驗(yàn)證檩坚。有了上面的原理着撩,那么UITablbeView滾動(dòng)的時(shí)候诅福,觸發(fā)WKWebView刷新頁面即可?可知的是睹酌,WKWebView是調(diào)用_updateVisibleContentRectAfterScrollInView:方法來對(duì)WKContentView來刷新內(nèi)容的权谁。

由下圖可知,而WKWebView的_updateVisibleContentRects方法實(shí)現(xiàn)憋沿,也只是調(diào)用了_updateVisibleContentRectAfterScrollInView:旺芽,也就是說直接調(diào)用WKWebView實(shí)例的_updateVisibleContentRects就可以刷新了。

下面辐啄,用RAC來監(jiān)聽一下UITableView實(shí)例的contentOffset屬性采章,在contentOffset發(fā)生變化的時(shí)候,也就是UITableView實(shí)例滾動(dòng)的時(shí)候壶辜,就去調(diào)用一下WKWebView實(shí)例的_updateVisibleContentRects方法去刷新需要渲染的內(nèi)容悯舟。

    @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);
        }
    }];

在運(yùn)行demo工程的時(shí)候,結(jié)果按照猜想的發(fā)生了砸民,滾動(dòng)到WKWebView下方時(shí)抵怎,原來會(huì)白屏的區(qū)域正常的渲染內(nèi)容了。

解決方案

上方猜想被證實(shí)了岭参,那么說這個(gè)方案時(shí)可以行的反惕,而且對(duì)源代碼的理解并沒有太大的偏差。按照原理演侯,可以使用一下幾個(gè)方法來解決白屏的問題

  • 用KVO方法監(jiān)聽UITableView的contnetOffset屬性姿染,contentOffset發(fā)生變化也就是說UITableView發(fā)生滾動(dòng),調(diào)用WKWebView實(shí)例的_updateVisibleContentRects秒际,刷新需要渲染的內(nèi)容
  • UITableView是繼承自UIScrollView的悬赏,在代碼中實(shí)現(xiàn)UIScrollView的delegate,在delegate實(shí)現(xiàn)中手動(dòng)調(diào)用WKWebView實(shí)例等UIScrollViewDelegate的方法娄徊,原理和第一種方法一樣
  • 使用CADisplayLink類闽颇,在CADisplayLink的回調(diào)方法里面調(diào)用WKWebView實(shí)例的_updateVisibleContentRects即可

上面三種方法的其實(shí)都是大同小異的,只是適合不同的場(chǎng)景寄锐。優(yōu)劣也不用說了进萄,一眼就能看出來了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锐峭,一起剝皮案震驚了整個(gè)濱河市中鼠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沿癞,老刑警劉巖援雇,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異椎扬,居然都是意外死亡惫搏,警方通過查閱死者的電腦和手機(jī)具温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筐赔,“玉大人铣猩,你說我怎么就攤上這事≤罘幔” “怎么了达皿?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贿肩。 經(jīng)常有香客問我峦椰,道長(zhǎng),這世上最難降的妖魔是什么汰规? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任汤功,我火速辦了婚禮,結(jié)果婚禮上溜哮,老公的妹妹穿的比我還像新娘滔金。我一直安慰自己,他們只是感情好茂嗓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布餐茵。 她就那樣靜靜地躺著,像睡著了一般在抛。 火紅的嫁衣襯著肌膚如雪钟病。 梳的紋絲不亂的頭發(fā)上萧恕,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天刚梭,我揣著相機(jī)與錄音,去河邊找鬼票唆。 笑死朴读,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的走趋。 我是一名探鬼主播衅金,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼簿煌!你這毒婦竟也來了氮唯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤姨伟,失蹤者是張志新(化名)和其女友劉穎惩琉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夺荒,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞒渠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年良蒸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伍玖。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫩痰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窍箍,到底是詐尸還是另有隱情串纺,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布仔燕,位于F島的核電站造垛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏晰搀。R本人自食惡果不足惜五辽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望外恕。 院中可真熱鬧杆逗,春花似錦、人聲如沸鳞疲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尚洽。三九已至悔橄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腺毫,已是汗流浹背癣疟。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留潮酒,地道東北人睛挚。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像急黎,于是被迫代替她去往敵國(guó)和親扎狱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容