• iOS WKWebView OC 与 JS 交互学习


    我写WKWebView 想让 服务端相应 一个 方法但是不响应,根据 UIWebView 用 JSContext就能拿到响应的处理经验是不是服务端 也需要 对 WKwebView有兼容的一个写法??? 特此学习 WKWebView 记录

    一 .WKWebView 代理协议

    (1)WKScriptMessageHandler :

    OC在JS调用方法时做的处理。如果需要调用对话窗口就会先执行(3)协议再执行 (1)协议

    好处:传递给OC的参数直接在字典里面,不用再在url里面拼凑后的结果去截取解析。

    必须实现

    - (void)userContentController:(WKUserContentController *)userContentController
          didReceiveScriptMessage:(WKScriptMessage *)message

    (2)WKNavigationDelegate:

    处理页面跳转和载入过程。

    #pragma mark -- 进行页面跳转
    
    // 接收到服务器跳转请求之后再执行
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler ;
    // 在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler ;
    #pragma mark -- 追踪加载过程
    
    // 页面开始加载时调用
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
    
    // 当内容开始返回时调用
    - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
    // 页面加载完成之后调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
    // 页面加载失败时调用
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
    
    //好多协议 按需添加。。。

    (3)WKUIDelegate

    这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框) 的网页交互

    #pragma mark - WKUIDelegate
    //警告
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(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;

    二.协议使用

    (1)WKScriptMessageHandler ,关键在当前页面销毁后要注销协议保证无强引用,当前页面也无法销毁,发生内存泄漏。

    //注册协议
        self.userContentController = [[WKUserContentController alloc] init];
        //注册方法
        WKDelegateController *delegateController = [[WKDelegateController alloc]init];
        delegateController.delegate = self;
        
        // 通过JS与webview内容交互
        config.userContentController = self.userContentController;
        // 注入JS对象名称HFAlert,HFConfirm,HFPrompt当JS通过HFAlert,HFConfirm,HFPrompt来调用时,
        // 我们可以在WKScriptMessageHandler代理中接收到
        [config.userContentController addScriptMessageHandler:delegateController name:HFAlert];
        [config.userContentController addScriptMessageHandler:delegateController name:HFConfirm];
        [config.userContentController addScriptMessageHandler:delegateController name:HFPrompt];
        [config.userContentController addScriptMessageHandler:delegateController name:HFShare];
    //注销协议
    - (void)dealloc
    {
        [self.userContentController removeScriptMessageHandlerForName:HFAlert];
        [self.userContentController removeScriptMessageHandlerForName:HFConfirm];
        [self.userContentController removeScriptMessageHandlerForName:HFPrompt];
        [self.userContentController removeScriptMessageHandlerForName:HFShare];
    }
    //实施协议
    - (void)userContentController:(WKUserContentController *)userContentController
          didReceiveScriptMessage:(WKScriptMessage *)message {
        NSLog(@"%@", message.body);
        if ([message.name isEqualToString:HFAlert]) {
            // 打印所传过来的参数,只支持NSNumber, NSString, NSDate, NSArray,
            // NSDictionary, and NSNull类型
            NSDictionary *dictionary = message.body;
            if (![dictionary isKindOfClass:[NSDictionary class]]) {
                return;
            }
            NSLog(@"%@",dictionary[@"body"]); //服务端传值 log
            dispatch_async(dispatch_get_main_queue(), ^{
                //主线程操作UI
            });
        } else if ([message.name isEqualToString:HFConfirm]) {
            
        } else if ([message.name isEqualToString:HFPrompt]) {
            
        } else if ([message.name isEqualToString:HFShare]) {
        
        }
    }
    设置代理 WKDelegateController 重写成代理控制器性质,保证设置代理后,可移除 的关键!!!
    //
    //  WKDelegateController.m
    //  SectionDemo
    //
    //  Created by HF on 2017/6/22.
    //  Copyright © 2017年 HF-Liqun. All rights reserved.
    //
    
    #import "WKDelegateController.h"
    
    @interface WKDelegateController ()
    
    @end
    
    @implementation WKDelegateController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
        if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
            [self.delegate userContentController:userContentController didReceiveScriptMessage:message];
        }
    }
    @end
    ////////
    //
    //  WKDelegateController.h
    //  SectionDemo
    //
    //  Created by HF on 2017/6/22.
    //  Copyright © 2017年 HF-Liqun. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    #import <WebKit/WebKit.h>
    
    @protocol WKDelegate <NSObject>
    
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
    
    @end
    
    @interface WKDelegateController : UIViewController <WKScriptMessageHandler>
    
    @property (weak , nonatomic) id < WKScriptMessageHandler > delegate;
    
    @end

    (2)WKNavigationDelegate,举例子和UIWebView shouldStart  方法一样,在请求前判断要不要继续执行

    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        NSString *hostname = navigationAction.request.URL.host.lowercaseString;
        if (navigationAction.navigationType == WKNavigationTypeLinkActivated
            && ![hostname containsString:@".baidu.com"]) {
            // 对于跨域,需要手动跳转
            [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
            
            // 不允许web内跳转
            decisionHandler(WKNavigationActionPolicyCancel);
        } else {
            self.progressView.alpha = 1.0;
            decisionHandler(WKNavigationActionPolicyAllow);
        }
        NSLog(@"%s", __FUNCTION__);
    }

    (3)WKUIDelegate  参考代码

    #pragma mark - WKUIDelegate 这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框) 的网页交互/**
     *  web界面中有弹出警告框时调用
     *
     *  @param webView           实现该代理的webview
     *  @param message           警告框中的内容
     *  @param frame             主窗口
     *  @param completionHandler 警告框消失调用 无回调
     */
    
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
        NSLog(@"%s", __FUNCTION__);
        //completionHandler(@"空回调");
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:[NSString stringWithFormat:@"JS调用alert message:%@",message] preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler();
        }]];
        
        [self presentViewController:alert animated:YES completion:NULL];
        NSLog(@"%@", message);
    }
    
    
    /**
     确认框
     
     @param webView 实现该代理的webview
     @param message 确认窗口内容
     @param frame 主窗口
     @param completionHandler 警告框消失调用 回传布尔变量
     */
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
        NSLog(@"%s", __FUNCTION__);
        
        //completionHandler(@"可以直接回传布尔变量");
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS调用confirm" preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(YES);
        }]];
        [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            completionHandler(NO);
        }]];
        [self presentViewController:alert animated:YES completion:NULL];
        
        NSLog(@"%@", message);
    }
    
    
    /**
     输入框
     
     @param webView web
     @param prompt 文本
     @param defaultText 默认输入文本
     @param frame frame
     @param completionHandler completionHandler  回传 字符串
     */
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
        
        //completionHandler(@"可以直接回传字符串");
        //可以根据回传参数来判断 是否必要展示 弹框,也可以隐式处理 根据当前参数判断回传指定逻辑参数
        NSLog(@"%s", __FUNCTION__);
        NSLog(@"%@", prompt);
        if ([prompt isEqualToString:HFPrompt]) {
            completionHandler(@"guess");
            return;
        }
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS调用输入框" preferredStyle:UIAlertControllerStyleAlert];
        [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.textColor = [UIColor redColor];
        }];
        
        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            completionHandler([[alert.textFields lastObject] text]);
        }]];
        
        [self presentViewController:alert animated:YES completion:NULL];
    }

    三.OC JS 互相调用

    (1)  OC 调用 JS 代码(OC注入JS)

            NSString *js = @"alertName('我是参数')";//可以向js传参数
    //NSString *js = @"callJsAlert()";//无参数方法 [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) { NSLog(@"response: %@ error: %@", response, error); NSLog(@"call js alert by native"); }];

    (2) JS 调用 OC方法:关键是按照约定方法名和注册名

    关键代码: 

    //name 为注册名 HFAlert HFPrompt ...  messageBody 为传递参数集合 和 UIWebView的 调用OC 方法 是不一样的!!!!!
    window.webkit.messageHandlers.<name>.postMessage(<messageBody>);

    参考示例:

    <!DOCTYPE html>
    <html>
      <head>
        <title>iOS and Js</title>
        <style type="text/css">
          * {
            font-size: 40px;
          }
        </style>
      </head>
      
      <body>
        
        <div style="margin-top: 100px">
          <h1>Test how to use objective-c call js</h1><br/><br/>
          <div><input type="button" value="call shareButton" onclick="shareClick()"></div>
          <br/><br/>
          <div><input type="button" value="call js alert" onclick="callJsAlert()"></div>
          <br/>
          <div><input type="button" value="Call js confirm" onclick="callJsConfirm()"></div><br/>
        </div>
        <br/>
        <div>
          <div><input type="button" value="Call Js prompt " onclick="callJsInput()"></div><br/>
          <div>Click me here: <a href="http://www.baidu.com">Jump to Baidu</a></div>
        </div>
        
        <br/>
        <div id="SwiftDiv">
          <span id="jsParamFuncSpan" style="color: red; font-size: 50px;"></span>
        </div>
        
        <script type="text/javascript">
          //JS执行window.webkit.messageHandlers.Share.postMessage(<messageBody>)
          function callJsAlert() {
            //WKUIDelegate 空回调 警告框代理被触发
            alert('Objective-C call js to show alert');
            // HFAlert是我们所注入的对象
            window.webkit.messageHandlers.HFAlert.postMessage({body: 'call js alert in js'});
          }
        
          function callJsConfirm() {
            //WKUIDelegate 布尔回调 选择确认框代理被触发
            if (confirm('confirm', 'Objective-C call js to show confirm')) {
            document.getElementById('jsParamFuncSpan').innerHTML
            = 'true';
          } else {
            document.getElementById('jsParamFuncSpan').innerHTML
            = 'false';
          }
          
          //HFConfirm是我们所注入的对象
          window.webkit.messageHandlers.HFConfirm.postMessage({body: 'call js confirm in js'});
        }
        
        function callJsInput() {
          //WKUIDelegate 字符串回调 输入框代理被触发
          var response = prompt('HFPrompt', 'Please input your name:');
          document.getElementById('jsParamFuncSpan').innerHTML = response;
          
           //HFPrompt是我们所注入的对象
          window.webkit.messageHandlers.HFPrompt.postMessage({body: response});
        }
        
        function shareClick() {
            var response = prompt('Hello');
            window.webkit.messageHandlers.HFShare.postMessage({body:response ,title:'测试分享的标题',content:'测试分享的内容',url:'https://github.com/maying1992'});
        }
    
        function alertName(msg) {
            document.getElementById('jsParamFuncSpan').innerHTML = 'name' + msg + ', nice to meet you';
        }
          </script>
      </body>
    </html>

    end

    参考 

    (1) http://www.tuicool.com/articles/qQRrMzY

    (2)http://www.cocoachina.com/ios/20160906/17487.html

    (3)http://www.jianshu.com/p/4fa8c4eb1316

    (4)http://blog.csdn.net/u011619283/article/details/52135988

     
  • 相关阅读:
    全球视角商讨Linux的将来生长三趋势
    Firefox 3.0新版应战IE欣赏器 年夜战在即
    讲解SQL与Oracle外键束厄狭窄中的级联删除
    在Oracle中添加用户 赋权 修正暗码 解锁
    一个完好的Oracle rman备份规复参考示例
    Novell即将面临FSF起诉 终了其发行Linux
    Linux之x登录前后的转变
    Ubuntu Linux 8.04 Vsftp 虚构用户设置
    mysql修复坏表的步履办法
    用UTL_INADDR包获取曾经衔接用户的IP地址
  • 原文地址:https://www.cnblogs.com/someonelikeyou/p/6890587.html
Copyright © 2020-2023  润新知