UIWebView和WKWebView與JS的通訊

簡介


UIWebView 可以在你的應用程序嵌入網頁內容并與之交互肴捉,但它笨重難用還有內存泄漏且。
WKWebView 代替了UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了統一的跨雙平臺 API珊蟀。它擁有高達60fps的滾動刷新率以及內置手勢忱辅,在性能死嗦、穩(wěn)定性举哟、功能方面有很大提升俯渤,支持了更多的HTML5特性,和 Safari 相同的 JavaScript 引擎侨嘀。

1. UIWebView


1.1 加載方法

//創(chuàng)建UIWebView
UIWebView  *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
[self.view addSubview:webView];
//加載URL
NSURL *url = [[NSURL alloc] initWithString:@"http://www.biadu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

加載方法有3種

- (void)loadRequest:(NSURLRequest *)request; //直接裝載URL
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;//加載HTML代碼
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;//用于轉載本地頁面或者外部傳來的NSData

1.2 代理回調

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;//進行加載前的預判斷臭挽,如果返回YES,則會進入后續(xù)流程
- (void)webViewDidStartLoad:(UIWebView *)webView;//開始加載網頁
- (void)webViewDidFinishLoad:(UIWebView *)webView;//加載完成
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;//加載失敗

1.3 Native與JS的相互調用

1.3.1 Native調用JS中的方法

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
//使用方法  JSFunctionName為JS內方法名稱 paramete是參數
[self. webView stringByEvaluatingJavaScriptFromString:@"JSFunctionName('paramete')"]

1.3.2 JS調用Native中的方法

JS調用Native方法會在其他線程中調用咬腕,所以更新UI則需要放在主線程中進行
#import <JavaScriptCore/JavaScriptCore.h>
可以給類添加JSExport協議欢峰,
凡是添加了JSExport協議類我們就可以通過JS調用到其方法變量等。

//此宏定義用于js調用oc方法
#define JSExportAs(PropertyName, Selector) \
   @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif

例如如下:在js中調用doFoo 即調用了本類的- (void)doFoo:(id)foo 方法

@textblock
    @protocol MyClassJavaScriptMethods <JSExport>
    JSExportAs(doFoo,
    - (NSString *)doFoo:(NSString *)foo
    );
    @end
@/textblock
//OC中的實現都需要加入 #import <JavaScriptCore/JavaScriptCore.h>
@property(nonatomic,strong)JSContext *jsContext;

//配置JSContext
-(void)configJsContext{
    if (_jsContext)  return;
    _jsContext  = [[self contentWebview] valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    JSExportObject *jsExportObject = [[JSExportObject alloc] init];
    jsExportObject.delegate = self;
    //把實現JSExport協議的對象添加到JSContext中的native對象doFoo方法 它會自動指定到 -(NSString *)doFoo:(id)foo這個方法當中去 且doFoo可以有NSString返回值
    _jsContext[@"native"] = jsExportObject;
}

//在OC中已經添加了native 對象涨共,可以在js中調用native 定義的
   var s= {
        methodId: i,
        params: p
    };
  native. doFoo(JSON.stringify(s))

2. WKWebView


2.1 加載方法

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
YFMWKWebView *wkWebView = [[YFMWKWebView alloc] initWithFrame:self.view.bounds configuration:config];
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.biadu.com"]];
[wkWebView loadRequest:mutableRequest];

加載方法有4種纽帖, 基本與UIWebView相同只是多了一項本地文件加載

//iOS 8
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
//iOS 9
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

2.2 代理回調

3種代理類型
WKNavigationDelegate( 提供了追蹤主窗口網頁加載過程和是否進行頁面加載新頁面的代理方法)
追蹤主窗口網頁加載過程

// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

是否進行頁面加載新頁面

// 接收到服務器跳轉請求之后調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到響應后,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在發(fā)送請求之前举反,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}

WKUIDelegate(用原生控件顯示網頁的方法回調)
主要用于WKWebView處理web界面的三種提示框(警告框懊直、確認框、輸入框)

//警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

WKScriptMessageHandler(提供從網頁中收消息的回調方法)
OC中接收JS的調用信息

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
 NSLog(@"JS 調用了 %@ 方法火鼻,傳回參數 %@",message.name, message.body);
}

2.3 Native與JS的相互調用

2.3.1 Native調用JS中的方法

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
//使用方法  JSFunctionName為JS內方法名稱 paramete是參數
[wkWebView evaluateJavaScript:@"JSFunctionName(‘paramete’)" completionHandler:^(id item, NSError * error) {
 
}];

2.3.2 JS調用Native中的方法

WKWebView中JS調用Native方法會在主線程中進行
如要進行JS調用Native需要進行WKWebView的配置 方法如下:
Native中需要添加配置信息室囊,最后把配置好的config添加到列表2.1中。

//創(chuàng)建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//添加消息處理  
WKUserContentController *controller = wkWebView.configuration.userContentController;
[controller addScriptMessageHandler:self name:@"callMe"];

Native向Web中注入自定義的JS代碼
在WKUserScript中添加自定義JS代碼魁索,然后提交到configuration.userContentController中

//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
        return window.webkit.messageHandlers.callMe.postMessage(i);\
        }";
        // 根據JS字符串初始化WKUserScript對象
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

JS中添加如下方法進行調用

//此方法內的<name>可以寫成ScriptMessageHandler中定義的name融撞,<messageBody>為要傳的信息內容
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

3. 問題解決


一 . 在使用WKWebView時如果你在dealloc打個斷點,如果發(fā)現沒有被釋放有可能是引起了循環(huán)引用問題(發(fā)生在addScriptMessageHandler方法中)
解決方法:
1.自定義一個WKScriptMessageHandler 采用weak方法實現代理方法
2.在離開頁面時添加removeScriptMessageHandlerForName這個方法進行移除操作

二. WKWebView JS調用OC 獲取返回值問題 因為- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 此方法沒有返回值所以不能實現返回值回調粗蔚。
方法一: 需要借用到OC調用JS方法邏輯重新調用JS中Callback方法來實現回調 參考本文2.3 或者使用

方法二:可以利用runJavaScriptTextInputPanelWithPrompt回調來實現尝偎。
首先注入如下代碼,主要實現所有調用OC方法都是通過prompt來實現

//把[configuration.userContentController addUserScript:self.script];添加到列表2.1中
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
          return prompt('Native_'+i);\
        }";
        // 根據JS字符串初始化WKUserScript對象
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

其次在OC中截獲Prompt并處理后返回數據

-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
    //如果是調用原生
    if (prompt && [prompt hasPrefix:@"Native_"]) {
        completionHandler(此處填寫處理內容);
    }else{
    //此處填寫自定義promot
    }
}```
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末支鸡,一起剝皮案震驚了整個濱河市冬念,隨后出現的幾起案子,更是在濱河造成了極大的恐慌牧挣,老刑警劉巖急前,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異瀑构,居然都是意外死亡裆针,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門寺晌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來世吨,“玉大人,你說我怎么就攤上這事呻征≡呕椋” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵陆赋,是天一觀的道長沐祷。 經常有香客問我嚷闭,道長,這世上最難降的妖魔是什么赖临? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任胞锰,我火速辦了婚禮,結果婚禮上兢榨,老公的妹妹穿的比我還像新娘嗅榕。我一直安慰自己,他們只是感情好吵聪,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布凌那。 她就那樣靜靜地躺著,像睡著了一般暖璧。 火紅的嫁衣襯著肌膚如雪案怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天澎办,我揣著相機與錄音嘲碱,去河邊找鬼。 笑死局蚀,一個胖子當著我的面吹牛麦锯,可吹牛的內容都是我干的。 我是一名探鬼主播琅绅,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼扶欣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了千扶?” 一聲冷哼從身側響起料祠,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎澎羞,沒想到半個月后髓绽,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡妆绞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年顺呕,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片括饶。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡株茶,死狀恐怖,靈堂內的尸體忽然破棺而出图焰,到底是詐尸還是另有隱情启盛,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站僵闯,受9級特大地震影響笤闯,放射性物質發(fā)生泄漏。R本人自食惡果不足惜棍厂,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望超陆。 院中可真熱鬧牺弹,春花似錦、人聲如沸时呀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谨娜。三九已至航攒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趴梢,已是汗流浹背漠畜。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坞靶,地道東北人憔狞。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像彰阴,于是被迫代替她去往敵國和親瘾敢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內容