OC與JS交互之WKWebView


 

上一篇文章我們使用了JavaScriptCore框架重寫了之前的示例,iOS8蘋果偏愛HTML5,重構了UIWebVIew,給我們帶來了WKWebView,使其性能、穩定性、功能大幅度提升,也更好的支持了HTML5的新特性。這篇文章就們就拿WKWebView來小試牛刀

 

一、WKWebView Framework

WKWebView的14個類與3個協議:

 

WKBackForwardList: 之前訪問過的 web 頁面的列表,可以通過后退和前進動作來訪問到。

WKBackForwardListItem: webview 中后退列表里的某一個網頁。

WKFrameInfo: 包含一個網頁的布局信息。

WKNavigation: 包含一個網頁的加載進度信息。

WKNavigationAction: 包含可能讓網頁導航變化的信息,用於判斷是否做出導航變化。

WKNavigationResponse: 包含可能讓網頁導航變化的返回內容信息,用於判斷是否做出導航變化。

WKPreferences: 概括一個 webview 的偏好設置。

WKProcessPool: 表示一個 web 內容加載池。 

WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。

WKScriptMessage: 包含網頁發出的信息。

WKUserScript: 表示可以被網頁接受的用戶腳本。 

WKWebViewConfiguration: 初始化 webview 的設置。

WKWindowFeatures: 指定加載新網頁時的窗口屬性。

WKWebsiteDataStore: 包含網頁數據存儲和查找。

 

WKNavigationDelegate: 提供了追蹤主窗口網頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法。

WKUIDelegate: 提供用原生控件顯示網頁的方法回調。

WKScriptMessageHandler: 提供從網頁中收消息的回調方法。

 

二、WKWebView中的三個代理方法 

1. 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;
// 在發送請求之前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

 

2. WKUIDelegate

創建一個新的WKWebView

// 創建一個新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

剩下三個代理方法全都是與界面彈出提示框相關的,針對於web界面的三種提示框(警告框、確認框、輸入框)分別對應三種代理方法。

// 界面彈出警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(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;

 

3. WKScriptMessageHandler

這個協議中包含一個必須實現的方法,這個方法是native與web端交互的關鍵,它可以直接將接收到的JS腳本轉為OC或Swift對象。

// 從web界面中接收到一個腳本時調用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

  

三、使用WKWebView重寫

這里我們和之前的界面做了一點改動,之前OC調用JS的時候是進行彈框處理,這里我在寫的時候,很郁悶,方法可以調用過去,但是唯獨js的alert方法調用沒有效果,所以這里采用了輸出到div的形式,並增加了一個clear按鈕

WKWebView不支持nib文件,所以這里需要使用代碼初始化並加載WebView 

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences.minimumFontSize = 18;

self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height/2) configuration:config];
[self.view addSubview:self.wkWebView];


NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSURL *baseURL = [[NSBundle mainBundle] bundleURL];
[self.wkWebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseURL];

OC端:

//1. JS調用OC 添加處理腳本
[userCC addScriptMessageHandler:self name:@"showMobile"];
[userCC addScriptMessageHandler:self name:@"showName"];
[userCC addScriptMessageHandler:self name:@"showSendMsg"];

// 在代理方法中處理對應事件
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    NSLog(@"%@",message.body);

    if ([message.name isEqualToString:@"showMobile"]) {
        [self showMsg:@"我是下面的小紅 手機號是:18870707070"];
    }
    
    if ([message.name isEqualToString:@"showName"]) {
        NSString *info = [NSString stringWithFormat:@"你好 %@, 很高興見到你",message.body];
        [self showMsg:info];
    }
    
    if ([message.name isEqualToString:@"showSendMsg"]) {
        NSArray *array = message.body;
        NSString *info = [NSString stringWithFormat:@"這是我的手機號: %@, %@ !!",array.firstObject,array.lastObject];
        [self showMsg:info];
    }
}

// 2. native調用js
- (IBAction)btnClick:(UIButton *)sender {
    if (!self.wkWebView.loading) {
        if (sender.tag == 123) {
            [self.wkWebView evaluateJavaScript:@"alertMobile()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
                //TODO
                NSLog(@"%@ %@",response,error);
            }];
        }
        
        if (sender.tag == 234) {
            [self.wkWebView evaluateJavaScript:@"alertName('小紅')" completionHandler:nil];
        }
        
        if (sender.tag == 345) {
            [self.wkWebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')" completionHandler:nil];
        }

    } else {
        NSLog(@"the view is currently loading content");
    }
}

JS端:

function clear() {
    document.getElementById('mobile').innerHTML = ''
    document.getElementById('name').innerHTML = ''
    document.getElementById('msg').innerHTML = ''
}

//OC調用JS的方法列表
function alertMobile() {
    //這里已經調用過來了 但是搞不明白為什么alert方法沒有響應
    //alert('我是上面的小黃 手機號是:13300001111')
    document.getElementById('mobile').innerHTML = '我是上面的小黃 手機號是:13300001111'
}

function alertName(msg) {
    //alert('你好 ' + msg + ', 我也很高興見到你')
    document.getElementById('name').innerHTML = '你好 ' + msg + ', 我也很高興見到你'
}

function alertSendMsg(num,msg) {
    //window.alert('這是我的手機號:' + num + ',' + msg + '!!')
    document.getElementById('msg').innerHTML = '這是我的手機號:' + num + ',' + msg + '!!'
}

//JS響應方法列表
function btnClick1() {
    window.webkit.messageHandlers.showMobile.postMessage(null)
}

function btnClick2() {
    window.webkit.messageHandlers.showName.postMessage('xiao黃')
}

function btnClick3() {
    window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
}

 

四、后記

  至此,整個系列的示例已完成,過程中收貨頗豐。每篇文章都會對知識點進行總結,在文章末尾給出相關鏈接和示例DEMO的地址,同樣本文的示例也已放在GitHub上,需要的同學取走不謝。關於這幾篇文章的DEMO,我匯總了下放在了360雲盤中,同學們也可以下載此壓縮包,解壓,對比學習,在看的過程中有什么呢疑問,歡迎在bolg下面留言,若發現文章中有那些地方沒有闡述清,或者沒有提到也可以留言告訴我

  隨着H5的強大,hybrid app已經成為當前互聯網的大方向,單純的native app和web app在某些方面顯得就很劣勢,2015年,Facebook在React.js Conf 2015大會上推出了基於JavaScript的開源框架React Native,使PhoneGap、Cordova時代徹底成為了過去式,React Native有着良好的原生控件的體驗,徹底擺脫了UIWebView那讓人不爽的性能和交互流程,而且具有良好的拓展和熱更新能力,它的功能遠遠不止這些,有興趣的同學,可以進入下面的 傳送門RN之不歸路

 

戳這里:本文的DEMO地址歡迎star

戳這里:系列示例DEMO合集:https://yunpan.cn/OcMmK58epCKjzn (提取碼:e525)

參考資料(戳這里):

>  http://nshipster.cn/wkwebkit/

>  http://www.huangyibiao.com/archives/742

>  https://github.com/reactnativecn/react-native-guide

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM