轉載:WKWebView使用過程中遇到的坑

轉載:http://www.lxweimin.com/p/85f1710c3b32

前言

在以前,一直以為Hybrid App開發是一種略顯簡單的事,不會使用太多能發揮移動端原生本身優勢的復雜API,后來在新公司的工作(半混合式開發)過程中,發現混合式開發也是很多坑... 或者說WKWebView好多坑...

以下所說的內容,參考鏈接上基本上都有,本文的敘述方式主要是結合自己的經歷(自己踩過的總結總是那么的深刻...(\捂臉))
應該在開始混合開發之前就看下這篇文章的,結果真的是等自己踩坑踩了一遍,總結之后,發現這篇文章上都有....(\大哭)
參考鏈接2: http://www.lxweimin.com/p/86d99192df68

目錄

  1. 加載URL的 encode問題
  2. loadRequest造成的body數據丟失
  3. 使用WKUserContentController造成的內存泄漏問題
  4. WKWebView的白屏問題(拍照引起)
  5. NSURLProtocol(做網頁緩存)
  6. WKWebView的截屏問題(做意見反饋)
  7. window.alert()引起的crash問題(暫時沒遇到)
  8. WKWebView攔截協議
  9. User-Agent修改
  10. UI細節問題
    . wkwebview中 h5絕對布局不生效
    . iOS 12中WKWebView中表單 鍵盤彈起自動上移,導致的兼容問題
    . WKWebview會下移20
    . 頁面滾動速率
    . 視頻自動播放
    . goBack API問題
    · didFinishNavigation遲遲不調用
  11. WKWebView忽略安全區&輸入框響應區上移問題

1. 加載URL的 encode問題

在數據網絡請求或其他情況下,需要把URL中的一些特殊字符轉換成UTF-8編碼,比如:中文。解決無法加載的問題

編碼:

iOS 9以前
stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding
ios9后對其方法進行了修改
stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]

解碼

iOS 9以前
stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding
iOS 9以后
stringByRemovingPercentEncoding

總結:混合開發中,最好將所有的URL的編解碼問題都交給前端或者后端來做,畢竟移動端發版太笨重了,最起碼保證iOS與Android兩端的處理一致,否則前端同學做處理就太麻煩了

2. loadRequest造成的body數據丟失

在 WKWebView 上通過 loadRequest 發起的 post 請求 body 數據會丟失:

//同樣是由于進程間通信性能問題,HTTPBody字段被丟棄[request setHTTPMethod:@"POST"];
[request setHTTPBody:[@"bodyData" dataUsingEncoding:NSUTF8StringEncoding]];
[wkwebview loadRequest: request];

解決方案:見參考鏈接,目前暫無使用場景

3. 使用WKUserContentController造成的內存泄漏問題

  self -> webView -> WKWebViewConfiguration -> WKUserContentController -> self (addScriptMessageHandler)

__weak typeof(self) copy_self = self;
addScriptMessageHandler: copy_self  //不能解決問題

解決方案:
  單獨創建一個類實現`WKScriptMessageHandler`協議,然后在該類中再創建一個協議,由self來實現協議
  即: `self -> webView -> WKWebViewConfiguration -> WKUserContentController -> weak delegate obj --delegate--> self `

示例代碼:
 1.創建一個新類WeakScriptMessageDelegate

  #import <Foundation/Foundation.h>
  #import <WebKit/WebKit.h>
  @interface WeakScriptMessageDelegate : NSObject
    @property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
    - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
  @end

  @implementation WeakScriptMessageDelegate

  - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
      self = [super init];
      if (self) {
        _scriptDelegate = scriptDelegate;
      }
      return self;
  }

  - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
      [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
  }

  @end

  2.在我們使用WKWebView的控制器中引入我們創建的那個類,將注入js對象的代碼改為:
  [config.userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:scriptMessage];

  3.在delloc方法中通過下面的方式移除注入的js對象
  [self.config.userContentController removeScriptMessageHandlerForName:scriptMessage];
  上面三步就可以解決控制器不能被釋放的問題了

4. WKWebView的白屏問題(拍照引起)

WKWebView 自詡擁有更快的加載速度,更低的內存占用,但實際上 WKWebView 是一個多進程組件,Network Loading 以及 UI Rendering 在其它進程中執行。
換WKWebView加載網頁后,App 進程內存消耗反而大幅下降,但是仔細觀察會發現,Other Process 的內存占用會增加。在一些用 webGL 渲染的復雜頁面,使用 WKWebView 總體的內存占用(App Process Memory + Other Process Memory)不見得比 UIWebView 少很多。

在 UIWebView 上當內存占用太大的時候,App Process 會 crash;
而在 WKWebView 上當總體的內存占用比較大的時候,WebContent Process 會 crash,從而出現白屏現象

# 解決方案:
  1. 借助 iOS 9以后 WKNavigtionDelegate 新增了一個回調函數:
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0));

當 WKWebView 總體內存占用過大,頁面即將白屏的時候,系統會調用上面的回調函數,我們在該函數里執行[webView reload](這個時候 webView.URL 取值尚不為 nil)解決白屏問題。在一些高內存消耗的頁面可能會頻繁刷新當前頁面,H5側也要做相應的適配操作。

  1. 檢測 webView.title 是否為空
    并不是所有H5頁面白屏的時候都會調用上面的回調函數,比如,最近遇到在一個高內存消耗的意見反饋H5頁面上 present 系統相機,拍照完畢后返回原來頁面的時候出現白屏現象(拍照過程消耗了大量內存,導致內存緊張,WebContent Process 被系統掛起),但上面的回調函數并沒有被調用。在WKWebView白屏的時候,另一種現象是 webView.titile 會被置空, 因此,可以在 viewWillAppear 的時候檢測 webView.title 是否為空來 reload 頁面。

注意:可能有的前端頁面確實沒寫title標簽,在前端移動端開發中是可能會有這種場景的,會造成頁面反復刷新

綜合以上兩種方法可以解決絕大多數的白屏問題。

5. NSURLProtocol(做網頁緩存)

WKWebView中NSURLProtocol的使用以及對H5的緩存,這是利用NSURLProtocol做網頁緩存以及帶來的隱患。

6. WKWebView的截屏問題(做意見反饋)

WKWebView 下通過 -[CALayer renderInContext:]實現截屏的方式失效,需要通過以下方式實現截屏功能:

@implementation UIView (ImageSnapshot) 
- (UIImage*)imageSnapshot { 
    UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor); 
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return newImage; 
} 
@end

然而這種方式依然解決不了 webGL 頁面的截屏問題,Safari 以及 Chrome 這兩個全量切換到 WKWebView 的瀏覽器也存在同樣的問題:對webGL 頁面的截屏結果不是空白就是純黑圖片

7. window.alert()引起的crash問題(暫時沒遇到)

8. WKWebView攔截協議

WKWebView內默認不允許iTunes、weixin等協議跳轉
UIWebView打開ituns.apple.com、跳轉到appStore,、撥打電話,、喚起郵箱等一系列操作UIWebView 自己處理不了會自動交給UIApplication 來處理。

WKWebView上述事件WKWebView 不會自動交給UIApplication 來處理,除此之外,js端通過window.open() 打開新的網頁的動作也被禁掉了

9. User-Agent修改

不要擅自修改webView的User-Agent,務必要跟前端反復確認,是否有用UA來做一些設備區分,進而做一些系統、機型適配問題。

10. UI細節問題

# 1. wkwebview中 h5絕對布局不生效
_baseWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; (ios 11之后)`

然后:前端需要在meta標簽中增加 **iPhoneX**的適配**---**適配方案**viewport-fit**:**cover**
# 2. iOS 12中WKWebView中表單 鍵盤彈起自動上移,導致的兼容問題

WKWebView會自動監聽鍵盤彈出,并做上下移動處理(效果如同IQKeyboardManage這些庫),但是在iOS12中會有一些問題,鍵盤收起后,控件不恢復原狀,或者部分控件消失等不兼容問題
解決方案:

if(kSystemVersion < 12.0) {
    if (@available(iOS 11.0, *)) {
        _webview.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } 
}
if (@available(iOS 12.0, *)) {
     _webview.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
}
# 3. WKWebview會下移20

解決方案:

VC.automaticallyAdjustsScrollViewInsets = NO;  //iOS11以及以后失效
需要使用_webview.scrollView.contentInsetAdjustmentBehavior
# 順帶解釋一下以下兩個屬性

關于extendedLayoutIncludesOpaqueBarsautomaticallyAdjustsScrollViewInsets

  • 這兩個屬性屬于UIViewController
  • 默認情況下extendedLayoutIncludesOpaqueBars = false 擴展布局不包含導航欄
  • 默認情況下automaticallyAdjustsScrollViewInsets = true 自動計算滾動視圖的內容邊距
  • 但是,當 導航欄 是 不透明時,而tabBar為透明的時候,為了正確顯示tableView的全部內容,需要重新設置這兩個屬性的值,然后設置contentInset(參考代碼).

在iOS11 中, UIViewController的automaticallyAdjustsScrollViewInsets屬性已經不再使用,我們需要使用UIScrollView的 contentInsetAdjustmentBehavior屬性來替代它.

UIScrollViewContentInsetAdjustmentBehavior 是一個枚舉類型,值有以下幾種:

  • automatic 和scrollableAxes一樣,scrollView會自動計算和適應頂部和底部的內邊距并且在scrollView 不可滾動時,也會設置內邊距.
  • scrollableAxes 自動計算內邊距.
  • never不計算內邊距
  • always 根據safeAreaInsets 計算內邊距一般我們肯定需要設置為 never,我們自己來控制間距,但是在iOS 12的webView中,就會出現開始所說的問題,需要設置為automatic才能解決

調整WKWebView布局方式,避免調整webView.scrollView.contentInset。實際上,即便在 UIWebView 上也不建議直接調整webView.scrollView.contentInset的值.

# 4. 頁面滾動速率

WKWebView 需要通過 scrollView delegate 調整滾動速率:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
 scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}
# 5. 視頻自動播放

WKWebView 需要通過WKWebViewConfiguration.mediaPlaybackRequiresUserAction設置是否允許自動播放,但一定要在 WKWebView 初始化之前設置,在 WKWebView 初始化之后設置無效。

# 6. goBack API問題

WKWebView 上調用 -[WKWebView goBack], 回退到上一個頁面后不會觸發window.onload()函數、不會執行JS。

# 7. didFinishNavigation遲遲不調用

明明看起來頁面加載完全,卻不調用(一般只發生在第一次進入該頁面)。
解決方法:經過自定義NSURLProtocol,攔截所有的H5加載資源,并在didCompleteWithError中打印資源的加載情況,發現有圖片資源,域名有問題
Error Domain=NSURLErrorDomain Code=-1003 "未能找到使用指定主機名的服務器。
DNS解析失敗導致系統認定H5一直沒加載完成,第二次再進入,系統緩存了DNS解析的映射記錄,所以很快就認定資源錯誤,調用了didFinish方法。

————————————————————補充————————————————————

11.WKWebView忽略安全區&輸入框響應區上移問題

在iOS12中,如果設置webView.scrollView.contentInsetAdjustmentBehavior = .never,則打開有input彈框輸入框的html時候,彈出鍵盤,輸入框會上移以便適配鍵盤,收回鍵盤輸入框會正常恢復原位置,但是輸入框的響應焦點還保留在上面不會恢復到原來位置。解決方案就是做如下設置webView.scrollView.contentInsetAdjustmentBehavior = .automatic。這樣輸入框焦點問題可以完美解決。

但是這引入了一個新的問題,當設置automatic后,WKWebView上部和下部出現了safearea區域,有時候我們想要網頁填充滿整個屏幕區域,又要使用automatic,又要忽略safearea,這是個問題。

最終在stackoverflow上找到了一種解決方案,如下所示。

import Foundation
import WebKit

class FullScreenWKWebView: WKWebView {
    override var safeAreaInsets: UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }
}

或者

extension WKWebView {
    override open var safeAreaInsets: UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }
}   

我們通過重寫WKWebView的safeAreaInsets屬性,返回新的安全區區域,這樣就會忽略掉安全區。??

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容