WKWebView js和oc交互详解

JS调用OC的方法

以下代码都在这个demo里。可以通过下面几种方式实现js调用oc。

1.拦截URL

WKWebview可以在代理方法里拦截假的URL,从URL中获取要调用的方法名字和参数。
html的代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="format-detection" content="telephone=no" />
        <title>分享页</title>
    </head>
    <body>
        <p></p>
        <div>
            <button onclick="showToast();">无参数弹框提示</button>
            <button onclick="showToastWithParameter();">有参数弹框提示</button>
            <button onclick="shareClick();">Click Me!</button>
        </div>
        <p></p>
        <script>
            function showToast() {
                window.location.href = 'test1://showToast';
            }
        
        function showToastWithParameter() {
            window.location.href = 'test1://showToastWithParameter?parama=666666';
        }
        
        //不使用window.location.href
        function loadURL(url) {
            var iFrame;
            iFrame = document.createElement("iframe");
            iFrame.setAttribute("src", url);
            iFrame.setAttribute("style", "display:none;");
            iFrame.setAttribute("height", "0px");
            iFrame.setAttribute("width", "0px");
            iFrame.setAttribute("frameborder", "0");
            document.body.appendChild(iFrame);
            // 发起请求后这个 iFrame 就没用了,所以把它从 dom 上移除掉
            iFrame.parentNode.removeChild(iFrame);
            iFrame = null;
        }
        function shareClick() {
            loadURL("Test2://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址");
        }
        </script>
    </body>
</html>

当用户点击按钮时,会在WKWebview的代理方法:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    NSURLRequest *request = [navigationAction request];
    NSString * scheme = request.URL.scheme;
    NSString * host = request.URL.host;
    NSString * query = request.URL.query;
    if ([scheme isEqualToString:@"test1"]) {
        NSString *methodName = host;
        if (query) {
            methodName = [methodName stringByAppendingString:@":"];
        }
        SEL sel = NSSelectorFromString(methodName);
        NSString *parameter = [[query componentsSeparatedByString:@"="] lastObject];
        [self performSelector:sel withObject:parameter];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
        
    }else if ([scheme isEqualToString:@"test2"]){//JS中的是Test2,在拦截到的url scheme全都被转化为小写。
        NSURL *url = request.URL;
        NSArray *params =[url.query componentsSeparatedByString:@"&"];
        NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
        for (NSString *paramStr in params) {
            NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
            if (dicArray.count > 1) {
                NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
                [tempDic setObject:decodeValue forKey:dicArray[0]];
            }
        }
        
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"方式一" message:@"这是OC原生的弹出窗" delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
        [alertView show];
        NSLog(@"tempDic:%@",tempDic);
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

2.scriptMessageHandler

这是Apple在WebKit里新增加的方法,位于WKUserContentController类里。

/*! @abstract Adds a script message handler.
 @param scriptMessageHandler The message handler to add.
 @param name The name of the message handler.
 @discussion Adding a scriptMessageHandler adds a function
 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) for all
 frames.
 */
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

/*! @abstract Removes a script message handler.
 @param name The name of the message handler to remove.
 */
- (void)removeScriptMessageHandlerForName:(NSString *)name;

第一个方法会在所有的frame里添加一个js的函数window.webkit.messageHandlers.<name>.postMessage(<messageBody>)。比如,我们在OC中添加一个handle:
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"shareNothing"]; ;
当我们在js中调用下面方法时:
window.webkit.messageHandlers.shareNothing.postMessage(null);//null必须要写
我们在OC中会收到WKScriptMessageHandler的回调:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"shareNothing"]) {
    }
}

当然,记得在适当的地方调用removeScriptMessageHandler:

[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareNothing"];

html代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="format-detection" content="telephone=no" />
        <title>分享页</title>
    </head>
    <body>
        <p></p>
        <div>
            <button onclick="share('分享标题', 'http://cc.cocimg.com/api/uploads/170425/b2d6e7ea5b3172e6c39120b7bfd662fb.jpg', location.href)">分享</button>
            <button onclick="test()">分享不带参数</button>

        </div>
        <p></p>
        <script>
            function share (title, imgUrl, link) {
                //便于WKWebView测试
                window.webkit.messageHandlers.shareTitle.postMessage({aTitle: title, aImgUrl: imgUrl, aLink: link});
                //这里需要OC实现
            }
            function test() {
            //便于WKWebView测试
                window.webkit.messageHandlers.shareNothing.postMessage(null);//null必须要写
            }
        
        </script>
    </body>
</html>

OC代码:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [_webView.configuration.userContentController addScriptMessageHandler:self name:@"shareTitle"];
    [_webView.configuration.userContentController addScriptMessageHandler:self name:@"shareNothing"];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareTitle"];
    [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareNothing"];
}

#pragma mark -- WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    
    NSString *name = message.name;
    id body = message.body;
    NSLog(@"%@", message.name);
    NSLog(@"%@", message.body);
    
    if ([name isEqualToString:@"shareTitle"]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    }
    
    if ([name isEqualToString:@"shareNothing"]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    }
}

WebViewJavascriptBridge

WebViewJavascriptBridge
是一个第三方的库用于和js交互的。它在做WKWebView的js调用oc方法的时候使用的是拦截假的url的方式。

DSBridge

DSBridge在做js调用oc的方法的时候,没有使用拦截URL和Message handle的方式。它的js方法会调用到ret = prompt("_dsbridge=" + method, arg);,这是js的一个弹框的方法,会调用到WKWebView的代理方法

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

在这个方法里再去调用oc的方法。

JS对象:

html代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script>
            function test(paramete){
                JSObject.share(paramete);
            }
        </script>
    </head>
    <body>
        <button onclick="test('1')" style="width: 80px;height: 35px;">分享</button>

        <div id='zsz'></div>
    </body>
</html>

html中用到了JSObject,这里oc里要添加自定义的脚本:

/**
 * Native为H5提供的Api接口
 *
 * @type {js对象}
 */
var JSObject = (function() {

    var NativeApi = {
        share: function(param) {
            //调用native端
            _nativeShare(param);
        },
    }
    function _nativeShare(param) {
        //js -> oc
        window.webkit.messageHandlers.shareTitle.postMessage(param);
    }

    //闭包,把Api对象返回
    return NativeApi;
})();

OC的代码:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self addNativeApiToJS];

}
- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareTitle"];
}
- (void)addNativeApiToJS
{
    //防止频繁IO操作,造成性能影响
    static NSString *nativejsSource;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        nativejsSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"NativeApi" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];
    });
    //添加自定义的脚本
    WKUserScript *js = [[WKUserScript alloc] initWithSource:nativejsSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    [self.webView.configuration.userContentController addUserScript:js];
    //注册回调
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"shareTitle"];
}

#pragma mark -- WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    
    NSString *name = message.name;
    id body = message.body;
    NSLog(@"%@", message.name);
    NSLog(@"%@", message.body);
    
    if ([name isEqualToString:@"shareTitle"]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    }
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容