WKWebView與Javascript的交互

最近在幫公司做新版本的時(shí)候夫植,在加載一個(gè)H5界面的時(shí)候椅挣,需要和Javascript交互逗扒,之前只是會(huì)粗略使用WKWebView乒融,今天就認(rèn)真地上網(wǎng)查閱了一下資料


iOS App與JavaScript交互的流程

前言

With iOS 8 Apple has added a ton of user-facing goodness. The Health app, Apple Pay, and expanded TouchID capabilities?—?just a few things everyday users will be happy about. On the sdk side they’ve added a lot of cool things as well, but one I’m excited about is the addition of WKWebView. This is very similar to the related–but less powerful–UIWebView available since iOS 2. UIWebView offers simple methods for loading a remote url, navigating forwards and back, and even running basic JavaScript. In contrast WKWebView offers a full-blown configuration (via WKWebViewConfiguration), navigation (via WKNavigationDelegate), estimated loading progress, and evaluating JavaScript.
譯文:相比于iOS2版本推出的UIWebView掰盘,iOS8推出的WKWebView提供了一系列的配置方案(WKWebViewConfiguration/WKNavigationDelegate),預(yù)估加載的進(jìn)度和JavaScript的交互

At a high level passing information from native code to the JavaScript runtime is done by calling the evaluateJavaScript method on a WKWebView object. You can pass a block to capture errors but I’m not exploring that here. Passing information from JavaScript land to the iOS application uses the overly verbose Objective-C-style window.webkit.messageHandlers.NAME.postMessage function where NAME is whatever you call the script handler.
譯文:通過調(diào)用WKWebView的evaluateJavaScript方法來實(shí)現(xiàn)傳遞信息摄悯,達(dá)到原生代碼和JavaScript通訊的效果,你可以通過一個(gè)block來捕獲錯(cuò)誤愧捕,但是我并不會(huì)去關(guān)注那個(gè)部分奢驯,當(dāng)你調(diào)用script handler(處理script操作)你要寫window.webkit.messageHandlers這類冗長(zhǎng)的充滿警告的語句來實(shí)現(xiàn)JavaScript發(fā)送信息到iOS的應(yīng)用

如何創(chuàng)建對(duì)象

創(chuàng)建WKWebView對(duì)象的交互

WKWebView instance (called webView)
WKWebViewConfiguration instance (called configuration)
WKUserContentController instance (called controller)
The constructor for WKWebView takes a configuration parameter. This allows an instance of WKWebViewConfiguration to be passed and additional settings configured. The important property is userContentController, an instance of WKUserContentController. This controller has a method called addScriptMessageHandler which is how messages from JavaScript land are sent to the native application. This is a big chunk of boilerplate that needs to get setup before the WKWebView can be loaded. Thankfully it’s not all bad.
Oh right, the ViewController needs to match the protocol defined by WKScriptMessageHandler. This means implementing the userContentController delegate method. Onwards to the code examples.
譯文:WKWebView的初始化需要傳入一個(gè)WKWebViewConfiguration參數(shù),因此你需要初始化WKWebViewConfiguration的對(duì)象來設(shè)置配置次绘。比較重要的就是userContentController這個(gè)屬性瘪阁,通過調(diào)用userContentController的addScriptMessageHandler方法來監(jiān)聽JavaScript的調(diào)用,這一步在初始化WKWebView之后就要加上監(jiān)聽 此外對(duì)應(yīng)的控制器需要implement一個(gè)協(xié)議(WKScriptMessageHandler),然后在WKScriptMessageHandler這個(gè)delegate的回調(diào)中處理

實(shí)例代碼

//ViewController.h
#import <WebKit/WebKit.h>
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <WKScriptMessageHandler>

@end
@interface ViewController ()

@property (nonatomic, strong) WKWebView *webView;

@end

#pragma mark - private method

//配置子控件
- (void)configView {
    [self.view addSubview:self.webView];
}

#pragma mark - WKScriptMessageHandler


- (void)userContentController:(WKUserContentController *)userContentController
      didReceiveScriptMessage:(WKScriptMessage *)message {
    
    // Log out the message received
    NSLog(@"Received event %@", message.body);
    
    // Then pull something from the device using the message body
    NSString *version = [[UIDevice currentDevice] valueForKey:message.body];
    
    // Execute some JavaScript using the result
    NSString *exec_template = @"set_headline(\"received: %@\");";
    NSString *exec = [NSString stringWithFormat:exec_template, version];
    [_webView evaluateJavaScript:exec completionHandler:nil];
}

#pragma mark - getter

- (WKWebView *)webView {
    if(_webView) {
        return _webView;
    }
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    
    WKUserContentController *controller = [[WKUserContentController alloc] init];
    configuration.userContentController = controller;
   [controller addScriptMessageHandler:self name:@"observe"];
    
    _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT-64) configuration:configuration];
    _webView.navigationDelegate = self;
    _webView.UIDelegate = self;
    
    return _webView;
}

另一種實(shí)例代碼(網(wǎng)上的demo)

<html>
    <!--描述網(wǎng)頁信息-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>小黃</title>
        <style>
            *{
                font-size: 50px;
            }
        
            .btn{height:80px; width:80%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1em; color: white}
        </style>
        
        <script>
            function clear() {
                document.getElementById('mobile').innerHTML = ''
                document.getElementById('name').innerHTML = ''
                document.getElementById('msg').innerHTML = ''
            }
        
            //OC調(diào)用JS的方法列表
            function alertMobile() {
                //這里已經(jīng)調(diào)用過來了 但是搞不明白為什么alert方法沒有響應(yīng)
                //alert('我是上面的小黃 手機(jī)號(hào)是:13300001111')
                document.getElementById('mobile').innerHTML = '我是上面的小黃 手機(jī)號(hào)是:13300001111'
            }

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

            function alertSendMsg(num,msg) {
                //window.alert('這是我的手機(jī)號(hào):' + num + ',' + msg + '!!')
                document.getElementById('msg').innerHTML = '這是我的手機(jī)號(hào):' + num + ',' + msg + '!!'
            }
        
            //JS響應(yīng)方法列表
            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 !!!'])
            }

        </script>
        
        
    </head>

    <!--網(wǎng)頁具體內(nèi)容-->
    <body>
        <br/>
        <div>
            <label>小黃:13300001111</label>
        </div>
        <br/>
        <div id="mobile"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick1()">小紅的手機(jī)號(hào)</button>
        </div>
        <br/>
        <div id="name"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick2()">打電話給小紅</button>
        </div>
        <br/>
        <div id="msg"></div>
        <div>
            <button class="btn" type="button" onclick="btnClick3()">發(fā)短信給小紅</button>
        </div>
    </body>
</html>
//
//  ViewController.m
//  OC與JS交互之WKWebView
//
//  Created by user on 16/8/18.
//  Copyright ? 2016年 rrcc. All rights reserved.
//

#import "ViewController.h"
#import <WebKit/WebKit.h>

@interface ViewController () <WKScriptMessageHandler>

@property (nonatomic, strong) WKWebView *wkWebView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    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];
    
    WKUserContentController *userCC = config.userContentController;
    //JS調(diào)用OC 添加處理腳本
    [userCC addScriptMessageHandler:self name:@"showMobile"];
    [userCC addScriptMessageHandler:self name:@"showName"];
    [userCC addScriptMessageHandler:self name:@"showSendMsg"];
}

//網(wǎng)頁加載完成之后調(diào)用JS代碼才會(huì)執(zhí)行邮偎,因?yàn)檫@個(gè)時(shí)候html頁面已經(jīng)注入到webView中并且可以響應(yīng)到對(duì)應(yīng)方法
- (IBAction)buttonClick:(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");
    }
}

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    NSLog(@"%@",message.body);

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

- (void)showMessage:(NSString *)message {
    [[[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
}

- (IBAction)clear:(id)sender {
    [self.wkWebView evaluateJavaScript:@"clear()" completionHandler:nil];
}

@end

總結(jié)

Objective-C 調(diào)用JavaScript 
使用方法:evaluateJavaScript: completionHandler:
JavaScript 調(diào)用 Objective-C管跺,app端接收回調(diào)
回調(diào)方法:(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
app端監(jiān)聽使用 addScriptMessageHandler:
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市禾进,隨后出現(xiàn)的幾起案子豁跑,更是在濱河造成了極大的恐慌,老刑警劉巖泻云,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艇拍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壶愤,警方通過查閱死者的電腦和手機(jī)淑倾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來征椒,“玉大人娇哆,你說我怎么就攤上這事〔龋” “怎么了碍讨?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蒙秒。 經(jīng)常有香客問我勃黍,道長(zhǎng),這世上最難降的妖魔是什么晕讲? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任覆获,我火速辦了婚禮,結(jié)果婚禮上瓢省,老公的妹妹穿的比我還像新娘弄息。我一直安慰自己,他們只是感情好勤婚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布摹量。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缨称。 梳的紋絲不亂的頭發(fā)上凝果,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音睦尽,去河邊找鬼器净。 笑死,一個(gè)胖子當(dāng)著我的面吹牛骂删,可吹牛的內(nèi)容都是我干的掌动。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼宁玫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼粗恢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起欧瘪,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤眷射,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后佛掖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妖碉,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年芥被,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欧宜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拴魄,死狀恐怖冗茸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匹中,我是刑警寧澤夏漱,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站顶捷,受9級(jí)特大地震影響挂绰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜服赎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一葵蒂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧重虑,春花似錦刹勃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伍宦。三九已至芽死,卻和暖如春乏梁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背关贵。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工遇骑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揖曾。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓落萎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親炭剪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子练链,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容