在開發過程中,經常會出現需要iOS移動端與H5混編的使用場景。 iOS中加載html網頁, 可以使用UIWebView或WKWebView. 本篇部落格将介紹兩種控件使用過程中如何實作OC與JS的互動。
UIWebView delegate協定方法//網頁即将開始加載
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;//網頁開始加載
- (void)webViewDidStartLoad:(UIWebView *)webView;//網頁加載完成
- (void)webViewDidFinishLoad:(UIWebView *)webView;//網頁加載失敗
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;//UIWebView自帶了一個方法, 可以直接調用JS代碼(轉化為string類型的js代碼)
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;//例如修改id為‘html’标簽内部的text屬性
[web stringByEvaluatingJavaScriptFromString:@"document.getElementById('html').innerText='修改内容'"];//也可以執行多行js代碼
[web stringByEvaluatingJavaScriptFromString:@"var div = document.getElementById('html'); div.innerText = '修改内容'"];
利用JavaScriptCore實作互動
JavaScriptCore中類及協定:
JSContext:給JavaScript提供運作的上下文環境
JSValue:JavaScript和Objective-C資料和方法的橋梁
JSManagedValue:管理資料和方法的類
JSVirtualMachine:處理線程相關,使用較少
JSExport:這是一個協定,如果采用協定的方法互動,自己定義的協定必須遵守此協定
OC中提供了JavaScriptCore 這個庫,使得OC與js的互動變得更加友善。
使用方法:
1 加入JavaScriptCore 這個framework
2 引入頭檔案
3 在VC裡面加入一個JSContext屬性
@property (strong, nonatomic) JSContext *context;
JSContext是什麼那? 我們看一下api裡面的解釋
@interface
@discussion A JSContextisa JavaScript execution environment. All
JavaScript execution takes place within a context, and all JavaScript values
are tied to a context.
大概意思是說:JSContext是一個JS的執行環境,所有的JS執行都發生在一個context裡面, 所有的JS value都綁定到context裡面
具體使用
//初始化context
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];//OC調用JS//(1)例如html的script中一個方法function dolike(a,b,c){
}//通過OC調用此方法NSString* method = @"dolike";
JSValue* function =[self.context objectForKeyedSubscript:method];//這裡面的a,b,c就是OC調用JS的時候給JS傳的參數
[function callWithArguments:@[a,b,c]];//JS調用OC//例如網頁中有個标簽,點選button的時候調用Jump方法, 此處3為傳入的參數
點我
//當點選網頁中的button的時候,觸發jump方法, 在OC中用如下代碼可以捕捉到jump方法, 并拿到JS給我傳的參數‘3’
self.context[@"jump"] = ^(NSString *str){//此處 str 值為'3'(js調用OC時傳給OC的參數)};
WKWebView :
說到WKWebView, 首先要說下WKWebView的優勢
1 更多的支援HTML5的特性
2 官方宣稱的高達60fps的滾動重新整理率以及内置手勢
3 将UIWebViewDelegate與UIWebView拆分成了14類與3個協定,以前很多不友善實作 的功能得以實作
4 Safari相同的JavaScript引擎
5 占用更少的記憶體
類:
WKBackForwardList: 之前通路過的 web 頁面的清單,可以通過後退和前進動作來通路到。
WKBackForwardListItem: webview 中後退清單裡的某一個網頁。
WKFrameInfo: 包含一個網頁的布局資訊。
WKNavigation: 包含一個網頁的加載進度資訊。
WKNavigationAction: 包含可能讓網頁導航變化的資訊,用于判斷是否做出導航變化。
WKNavigationResponse: 包含可能讓網頁導航變化的傳回内容資訊,用于判斷是否做出導航變化。
WKPreferences: 概括一個 webview 的偏好設定。
WKProcessPool: 表示一個 web 内容加載池。
WKUserContentController: 提供使用 JavaScript post 資訊和注射 script 的方法。
WKScriptMessage: 包含網頁發出的資訊。
WKUserScript: 表示可以被網頁接受的使用者腳本。
WKWebViewConfiguration: 初始化 webview 的設定。
WKWindowFeatures: 指定加載新網頁時的視窗屬性。
協定:
WKNavigationDelegate: 提供了追蹤主視窗網頁加載過程和判斷主視窗和子視窗是否進行頁面加載新頁面的相關方法。
WKScriptMessageHandler: 提供從網頁中收消息的回調方法。
WKUIDelegate: 提供用原生控件顯示網頁的方法回調。
加載方式://方式一
WKWebView *webView =[[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];//方式二
WKWebViewConfiguration * configuration =[[WKWebViewConfiguration alloc] init];
webView=[[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];
協定方法介紹:#pragma mark - 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{
NSLog(@"%@",navigationResponse.response.URL.absoluteString);//允許跳轉
decisionHandler(WKNavigationResponsePolicyAllow);//不允許跳轉//decisionHandler(WKNavigationResponsePolicyCancel);
}//在發送請求之前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"%@",navigationAction.request.URL.absoluteString);//允許跳轉
decisionHandler(WKNavigationActionPolicyAllow);//不允許跳轉
decisionHandler(WKNavigationActionPolicyCancel);
}#pragma mark - WKUIDelegate
//建立一個新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{return[[WKWebView alloc]init];
}//輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler{
completionHandler(@"http");
}//确認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
completionHandler(YES);
}//警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
NSLog(@"%@",message);
completionHandler();
}
OC與JS的互動
WKWebView
WKWebView的 UIDelegate 提供了三個協定方法, 可以讓前端很友善的攔截JS的alert, confirm, prompt方法。除此之外,OC,JS互調可以按照如下方法。
1 OC 調用JS
可以使用webkit這個庫
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError *_Nullable error))completionHandler;//例如OC調用JS的方法 setName
[webView evaluateJavaScript:@"setname('張三')"completionHandler:nil];//此處 setname為JS定義的方法名, 内部 ‘張三’為傳給JS的參數。 如果setname方法需要傳入一個json或者array等非字元參數, 需要用format方法将其轉為string類型,在調用evaluate方法。例如
NSString* para = [NSString stringWithFormat:@"setname('%@')",json];
JS調用OC
此時就要用到WKScriptMessageHandler了
//首先.m中加入屬性
@property (nonatomic ,strong)WKUserContentController *userCC;//1 遵循WKScriptMessageHandler協定//2 初始化
WKWebViewConfiguration * config =[[WKWebViewConfiguration alloc]init];
self.wkWebViw=[[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
[self.wkWebViw loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.webPageUrl]]];
[self.view addSubview:self.wkWebViw];
self.userCC=config.userContentController;
[self.userCC addScriptMessageHandler:self name:@"callOSX"];//此處相當于監聽了JS中callFunction這個方法
[self.userCC addScriptMessageHandler:self name:@"callFunction"];//當JS發出callFunction這個方法指令的時候, WKScriptMessageHandler的協定方法中我們就會收到這個消息
#pragma mark WKScriptMessageHandler delegate
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{//這個回調裡面, message.name代表方法名(‘本例為 callFunction’), message.body代表JS給我們傳過來的參數
}//最後, VC銷毀的時候一定要把handler移除
-(void)dealloc
{
[_userContentController removeScriptMessageHandlerForName:@"callFunction"];
}//對應的JS代碼
點我
window.webkit.messageHandlers.callFunction.postMessage(string)
}