基於WebKit完美支持JS交互和混編的WebView(VDWebView)

WKWebView
VDWebView的源碼和使用示例

VDWebView提供了最全的API調用和最方便的JS交互方式,可經過pod更新迭代;設計方案爲Protocol和Target-Action;有任何意見或者問題歡迎指出。html

CocoaPods

pod 'VDWebView', '~> 1.0.3'
複製代碼

基本描述

  • 封裝WebKit所提供的WKWebView,提供熟悉的代理方法(類UIWebViewDelegate)
  • 更加方便和安全的JS與OC方法相互調用(後面我會具體說明解決方案)
    • 支持以target-action的方式替代delegate(二者任意選擇)
    • 不會出現相似於使用WKWebView註冊OC方法忘記註銷致使循環引用沒法釋放的問題
  • 提供加載進度條的使用、預估進度值的讀取等
  • cookie的操做
  • iOS和Android通用的JS與OC交互方法(經過請求攔截實現)

爲何使用WKWebView

  • WKWebView是iOS8後推出的WebKit框架中的控件,因爲iOS12後已經棄用UIWebView了並且如今的大多數項目只適配到iOS8
  • 加載速度優於UIWebView且解決了加載網頁時的內存泄露問題
  • 在和JS交互方面提供了橋樑WKUserContentController
  • 沒好處這東西出來幹嗎,因此綜上用起來吧

提供了哪些更加熟悉和更加便捷的屬性和方法

VDWebViewDelegate

  • UIWebView代理方法,處理對象(WKNavigationDelegate)
/// 類UIWebView代理方法
- (void)webViewDidStartLoad:(VDWebView *)webView;
- (void)webViewDidFinishLoad:(VDWebView *)webView;
- (void)webView:(VDWebView *)webView didFailLoadWithError:(NSError *)error;
- (BOOL)webView:(VDWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
複製代碼
  • WKUIDelegateWKScriptMessageHandler代理的合併
/**
 JS調原生代理方法(註冊了過的方法將所有經過此方法回調)
 */
- (void)webView:(VDWebView *)webView didReceiveScriptMessage:(WKScriptMessage *)message;

/**
 JS彈框攔截方法--若是須要自定義彈框建議聲明此方法
 */
- (void)webView:(VDWebView *)webView showAlertWithType:(VDJSAlertType)type title:(NSString *)title content:(NSString *)content completionHandler:(void (^)(id))completionHandler;
複製代碼

新增了哪些屬性和方法

詳情可見VDWebViewProtocoljava

///預估網頁加載進度
@property (nonatomic, readonly) CGFloat estimatedProgress;
// 是否顯示進度條 默認不顯示
@property (nonatomic, assign) BOOL isShowProgressBar;
///進度條
@property (nonatomic, strong) UIView *progressBar;
/**
 web頁面加載完畢後的內容高度(在頁面加載完成後獲取)
 */
@property (nonatomic, readonly) CGFloat *contentHeight;
/// 是否啓用js調用原生彈框 默認爲NO 禁止(默認加載彈框在根試圖)
@property (nonatomic, assign) BOOL enableAllAlert;
@property (nonatomic, assign) BOOL enableAlert;
@property (nonatomic, assign) BOOL enableConfirm;
@property (nonatomic, assign) BOOL enablePrompt;

///back 層數
- (NSInteger)countOfHistory;
- (void)gobackWithStep:(NSInteger)step;

複製代碼

JS調用OC方法的綁定

在使用WKWebView時咱們須要調用WKWebViewconfiguration中的userContentController所屬類WKUserContentController提供的實例方法進行註冊,具體方法以下:git

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
複製代碼

對應的註銷方法爲:github

- (void)removeScriptMessageHandlerForName:(NSString *)name;
複製代碼

已知的循環引用問題

在使用addScriptMessageHandler:name:方法註冊時傳入的這個handler被循環引用,若是不調用對應的註銷方法就會致使handler這個對象沒法被釋放,若是你這個handler傳入是webView所在的控制器,那麼你就要在銷燬這個控制器前註銷掉你註冊的方法.web

tip: 如何知道控制器有沒有被釋放,重寫dealloc(),沒走此方法說明未被釋放json

VDWebView是如何解決循環引用問題

簡要分析可分爲下面三步安全

  • 使用VDScripMessageHandler做爲註冊的handler
    • 繼承協議WKScriptMessageHandler
    • 提供target-action回調方式
  • 保存註冊記錄
  • VDWebViewdealloc()方法中獲取註冊記錄並註銷

這些作的好處在於你在使用VDWebView時無需本身去一個個手動註銷了(若是你註冊的方法多的話那就是噩夢了)bash

VDWebView是如何進行方法的註冊和回調的

  • 方法的註冊
- (void)addScriptMessageHandler:(id)scriptMessageHandler name:(NSString *)name;
複製代碼
  • JS調用說明
// 沒效果可以使用try-catch
window.webkit.messageHandlers.#OC方法名#.postMessage(#參數#)

複製代碼

回調方式分兩種:delegate和target-action; 兩種方式只能存一,優先delegatecookie

  • delegate方式,只需在控制器中聲明VDWebViewDelegate中的方法
- (void)webView:(VDWebView *)webView didReceiveScriptMessage:(WKScriptMessage *)message;
複製代碼
  • target-action方式
    • 不能聲明上述的代理方法
    • 在控制器(方法註冊傳入的scriptMessageHandler)中聲明同名的OC方法

爲何要增長target-action的方式

target-action:目標-動做模式,拜C語言所賜,更是靈活不少,編譯期沒有任何檢查,都是運行時的綁定框架

  • VDWebView中就是經過NSSelectorFromString()動態加載方法,再經過NSMethodSignatureNSInvocation進行方法的簽名和調用
  • 這樣就能夠充分的體現JS調用對應的OC方法和一對一更加清晰和方便處理

JS的調用和注入

可經過兩種方式進行JS方法的調用,推薦第一種

  • WKWebView的同名方法
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;

複製代碼
  • UIWebView的同名方法(不建議使用這個辦法,由於會在內部等待執行結果)
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)javaScriptString;
複製代碼

腳本的注入和移除

/**
 注入腳本(js...)
 */
- (void)addUserScriptWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)mainFrameOnly;

/**
 移除全部注入的腳本
 */
- (void)removeAllUserScripts;
複製代碼

cookie的處理(待優化)VDWebViewCookiesProtocol

提供cookie的共享

因爲WKWebView的cookie是和NSHTTPCookieStorage不共享,這就形成使用WKWebView打開的web頁面沒法獲取到經過原生請求登陸的cookie,固然其它解決方案有不少種,好比

  • 經過URL的拼接把登陸信息傳遞過去
  • 經過js方法傳值

可是用了VDWebView就不須要考慮cookie的問題了,由於它已經默認把cookie帶過去了,固然你也能夠手動去關閉

/**
 不一樣步NSHTTPCookieStorage存儲的cookies 默認同步:NO
 同步NSHTTPCookieStorage中的cookie到WKWebView中,有可能會污染WKWebView中的cookie管理
 */
@property (nonatomic, assign)BOOL httpCookiesDisable;
複製代碼

對cookie的操做

/**
 設置cookie
 */
- (void)setCookieWithKey:(NSString *)key value:(NSString *)value expires:(NSTimeInterval)expires domain:(NSString *)domain;
- (void)setCookies:(NSString *)cookies;
/**
 獲取cookie
 */
- (NSArray *)getCookies;
複製代碼

iOS和Android通用的JS與OC交互方法VDWebViewJSBridge

關於使用方法

你只須要調用VDWebView繼承的協議VDWebViewProtocol所提供的初始化方法bridgeInitialized便可

// 調用初始化 在webView的控制器中實現同名方法
self.webView.delegate = self;
[self.webView bridgeInitialized];
複製代碼

js調用原生

與對應的js配套使用,此方案適用於iOS和Android對應的js文件以下

VDJSWebBridge.js傳送門

// 在js中直接調用VDJSWebBridge.js提供的方法,詳情請查看js
// methodName爲控制器中聲明的方法名,params爲json字符串
vd_jsBridge(methodName, params)
複製代碼

從JS方法觸發開始整個調用的過程以下

  1. 對params進行了base64編碼
  2. 最終的請求URL格式爲 vdjsbridge://methodName?params=base64(params)
  3. APP攔截請求,解析URL匹配scheme(vdjsbridge),獲取方法名,base64解碼和json轉對象獲取參數值
  4. 接受到第三步獲取的方法名和參數後經過Target-Action方法調用對應的方法

OC調用JS方法

// JS 方法
function jsMethod(param1, param2, param3) {
        alert("使用jsBridge調用js方法成功參數爲:"+param1+param2+param3)
        return "success";
    }

// OC調用JS方法
[self.webView.bridge executeJsMethod:@"jsMethod" params:@[@"1",@"2",@"3"] completionHandler:^(id result, NSError *error) {
        NSLog(@"\njs方法執行h結果回調:%@\n錯誤信息:%@",result,error);
    }];

複製代碼

後續版本思考和設計中

  • cookie的處理
  • 攔截webView內部請求經過自定義URL的方式進行JS交互
  • APP和web資源共享問題:好比圖片
相關文章
相關標籤/搜索