美文网首页
iOS开发---OC和JS交互一:WKWebView和OC的交互

iOS开发---OC和JS交互一:WKWebView和OC的交互

作者: 那时J花开 | 来源:发表于2019-04-10 17:24 被阅读0次

    上一篇文章中,我总结了UIWebViewNative进行交互的三种常用方式. 今天继续总结WKWebViewNative进行交互的方式.

    苹果在iOS8以后推出了WebKit框架, WKWebView用来负责网页的渲染、展示. 与UIWebView相比, WKWebView更加快捷, 内存占用更小, 也支持更多的HTML特性.

    一、协议拦截

    (1)、JSToNative
    • 逻辑: 点击HTML中的按钮, 传递参数到Native, Native展示接收到的数据

    • 实现: JS调用OC并传递参数
    • JS代码:

    <div style="margin-top: 20px">
            <button onclick = "buttonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
                点我~
            </button>
        </div>
    
    function buttonClick() {
                var message = '获取到html按钮点击事件';
                clickSuccess(message);
            }
            function clickSuccess(message) {
                var param = 'JS传递过来了参数:1234';
                JSTransferOC(message, param);
            }
        
            function JSTransferOC(message, param) {
                var url = 'JSTransferOC://' + message +'?'+ param;
                window.location.href = url;
            }
    
    • OC代码:
    #import <WebKit/WebKit.h>
    
    #pragma mark - WKNavigationDelegate
    
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        
        //!< 判断是否是约定好的scheme
        if ([navigationAction.request.URL.scheme caseInsensitiveCompare:@"JSTransferOC"] == NSOrderedSame) {
            [self presentAlertWithTitle:navigationAction.request.URL.host message:navigationAction.request.URL.query handler:^(UIAlertAction * _Nonnull action) {
                decisionHandler(WKNavigationActionPolicyCancel);
            }];
        } else {
            decisionHandler(WKNavigationActionPolicyAllow);
        }
        //!< 注意这里的 decisionHandler()必须调用, 否则会崩溃. 相当于UIWebView中的 - shouldStartLoadWithRequest 方法中 返回YES Or NO 是否允许跳转
    }
    
    • 原理:
      • JS与Native定义好入口函数- JSTransferOC();
      • OC遵循< WKNavigationDelegate >协议, 实现- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler代理方法, 这个代理方法在webView每次load前都会调用;
      • 在代理方法中, 拦截每次请求, 判断request.url.scheme是否是约定好的scheme;
      • 如果是约定好的scheme, 那么Native解读url, 读取参数;
    (2)、Native调用JS, 并传递参数
    • 逻辑: 点击Native登录按钮, 登录成功后, 将数据传递给JS, JS展示获取的数据.

    • 实现: Native将获取的数据传递给JS
    • JS代码:

     <div style = "font-size: 30px;margin-top: 20px">
            回调数据展示:
        </div>
    
        <div id = "returnValue" style = "font-size: 30px; border: 1px dotted; height: 100px;margin-top: 20px">
    
        </div>
    
    //!< OC调用JS, 并回传给JS参数, JS展示
            function OCTransferJS(message, param) {
                document.getElementById('returnValue').innerHTML = message + param;
            }
    
    • OC代码:
    - (void)login:(UIButton *)sender {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //!< 注意: evaluateJavaScript 只有在webView全部load完成后才能执行
            [self.webView evaluateJavaScript:[NSString stringWithFormat:@"OCTransferJS('OC调用JS代码, 并传递参数;', 'OC传递来了参数:12345678')"] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
            }];
        });
    }
    
    • 原理:
      • JS和Native定义好函数OCTransferJS, 作为入口函数;
      • Native通过-evaluateJavaScript: completionHandler:方法, 执行一段JS代码, 传递参数,
      • JS展示传递来的参数;

    二、WKScriptMessageHandler协议

    WKScriptMessageHandler, 官方释义为: A class conforming to the WKScriptMessageHandler protocol provides a method for receiving messages from JavaScript running in a webpage, 为遵循了WKScriptMessageHandler协议的类, 提供了一个用于从webpage网页中运行的JavaScript接收消息的方法

    (1)、JSToNative
    • 逻辑: 点击HTML中的按钮, 传递参数到Native, Native接收参数并展示;

    • 实现: JSToNative
    • JS代码:

    <div style="margin-top: 20px">
            <button onclick = "WKScriptMessageHandlerBtnClick()" style = "font-size: 30px; width: 500px; height: 100px" >
                WKScriptMessageHandlerBtn
            </button>
        </div>
    
    //!< 通过WKScriptMessageHandler的方式进行交互
            function WKScriptMessageHandlerBtnClick() {
                var message = '获取到HTML按钮点击事件';
                clickComplete(message);
            }
            function clickComplete(message) {
                var param = 'JS传递来了参数:12345';
                //!< 与webKit进行交互时, JS代码中要这样调用
                window.webkit.messageHandlers.JSToOC.postMessage(message + '     ' + param);
            }
    
    • OC代码:
    #import <WebKit/WebKit.h>
    @interface WKWebViewVC ()<WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler>
    
    //!< 为WKUserContentController添加name为'JSToOC'的 ScriptMessageHandler;
        WKUserContentController *userC = [[WKUserContentController alloc] init];
        [userC addScriptMessageHandler:self name:@"JSToOC"];
        
        //!< 配置WKWebViewConfiguration;
        WKWebViewConfiguration *configuretion = [[WKWebViewConfiguration alloc] init];
        configuretion.userContentController = userC;
        
        //!< 使用WKWebViewConfiguration初始化
        self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuretion];
    
    #pragma mark - WKScriptMessageHandler
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        if ([message.name caseInsensitiveCompare:@"JSToOC"] == NSOrderedSame) {
            [self presentAlertWithTitle:message.name message:message.body handler:nil];
        }
    }
    
    • 原理:
      • JS和Native约定好入口函数:JSToOC;
      • JS中通过window.webkit.messageHandlers.JSToOC.postMessage(message + ' ' + param)的方式, 调用JSToOC函数;
      • OC通过- addScriptMessageHandler: name:方法来监听nameJSToOC的方法;
      • OC类遵循WKScriptMessageHandler协议, 并实现代理方法- userContentController: didReceiveScriptMessage:, 在代理方法中获取数据message.body;

    注: - addScriptMessageHandler: name:会引起循环引用, 应该在合适的时间去-removeScriptMessageHandlerForName:;

    (2)、NativeToJS:

    和上述方法相同, 都是通过-evaluateJavaScript:completionHandler:来执行一段JS字符串;

    三、WKUIDelegate协议

    经常有在群里看到朋友抱怨, WKWebView中, JS调用alert()confirm()prompt()函数没有反应。 这是由于WKWebView的特性, native必须实现WKUIDelegate的三个代理方法, 才能正常弹出提示框.

    (1)、提示框的弹出
    • 逻辑: 点击html中的相应按钮, 在native中弹出提示框;

    • 实现: JS通过WKWebView调用弹窗
    • JS代码:

    <div id = "returnValue" style = "font-size: 30px; border: 1px dotted; height: 100px;margin-top: 20px">
        </div>
    
    <div style="margin-top: 20px">
            <button onclick = "alertButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
                alertButton
            </button>
        </div>
    
        <div style="margin-top: 20px">
            <button onclick = "confirmButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
                confirmButton
            </button>
        </div>
    
        <div style="margin-top: 20px">
            <button onclick = "promptButtonClick()" style = "font-size: 30px; width: 300px; height: 100px" >
                promptButton
            </button>
        </div>
    
    function alertButtonClick() {
               var result = alert('点击了HTML中的alertButton');
               document.getElementById('returnValue').innerHTML = result;
            }
        
            function confirmButtonClick() {
               var result = confirm('点击了HTML中的confirmButton');
               document.getElementById('returnValue').innerHTML = result;
            }
        
            function promptButtonClick() {
               var result = prompt('点击了HTML中的promptButton', '确定');
               document.getElementById('returnValue').innerHTML = result;
               
            }
    
    • OC代码:
      • alert:
    #pragma mark - WKUIDelegate
    
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
        [self presentAlertWithTitle:@"title" message:message handler:^(UIAlertAction * _Nonnull action) {
            completionHandler();
        }];
        
    }
    
    • confirm:
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Confirm" message:message preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(NO);
        }];
        UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(YES);
        }];
        [alertController addAction:cancelAction];
        [alertController addAction:confirmAction];
        [self presentViewController:alertController animated:YES completion:nil];
    }
    
    • TextInputPanel:
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
        
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert];
        [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = defaultText;
        }];
        UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(alertController.textFields[0].text);
        }];
        [alertController addAction:confirmAction];
        [self presentViewController:alertController animated:YES completion:nil];
        
    }
    
    • 原理:
      • html中创建各个弹窗需要的按钮, 并调用弹窗的方法;
      • native中实现WKUIDelegate中的代理方法, 调用原生弹窗;

    相关文章

      网友评论

          本文标题:iOS开发---OC和JS交互一:WKWebView和OC的交互

          本文链接:https://www.haomeiwen.com/subject/ihdoiqtx.html