美文网首页
iOS开发技巧之:WKWebView 与 JS 交互和基本使用

iOS开发技巧之:WKWebView 与 JS 交互和基本使用

作者: VKOOY | 来源:发表于2022-09-28 10:31 被阅读0次
    导入库
    #import <WebKit/WebKit.h>
    
    1、WKWebView 创建 不注册 js 调用 OC 的方法
    //初始化
     _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) ];
    // UI代理
    _webView.UIDelegate = self;
    // 导航代理
    _webView.navigationDelegate = self;
    // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
    _webView.allowsBackForwardNavigationGestures = YES;
    //可返回的页面列表, 存储已打开过的网页 
    //  WKBackForwardList * backForwardList = [_webView backForwardList];
    
    Webview 加载本地HTML
     NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil];
    NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    //加载本地html文件
    [_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
    
    Webview 加载网络HTML
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.chinadaily.com.cn"]];
    [request addValue:[self readCurrentCookieWithDomain:@"http://www.chinadaily.com.cn"] forHTTPHeaderField:@"Cookie"];
    [_webView loadRequest:request];
    
    Webview 页面后退、前进和刷新
    // 是否可以后退,返回 BOOL  YES 可以后退, NO 第一页,不可以后退,可以做 pop webView  操作
    if ([_webView canGoBack]) {
    //页面后退
        [_webView goBack];
    }
    else {
    // pop webView 退出 webView 页面
    }
    // 是否可以前进,返回 BOOL  YES 可以前进, NO 第一页,不可以前进
    if ([_webView canGoForward]) {
        //页面前进
        [_webView goForward];
    }
    else {
    // 没有下一页
    }
    //刷新当前页面
    [_webView reload];
    
    2、WKWebView 创建注册 js 调用 OC 的方法,

    \color{red} {jsCallOCMethodNames} 是一个 js 调用 OC 的方法名称的数组, 这些方法是不带返回值的,即void 方法,可以带参数
    例如:JSCallOCMethod1(‘jsonString’){ }

    jsCallOCMethodNames = @[@"JSCallOCMethod1",@"JSCallOCMethod2"];
    
    防止循环引用 WebViewScriptMessageDelegate 所以创建一个弱引用的 WeakWebViewScriptMessageDelegate
    // WKWebView 内存不释放的问题解决
    @interface WeakWebViewScriptMessageDelegate : NSObject<WKScriptMessageHandler>
        
    //WKScriptMessageHandler 这个协议类专门用来处理JavaScript调用原生OC的方法
    @property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
        
    - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
        
    @end
    
    @implementation WeakWebViewScriptMessageDelegate
        
    - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
        self = [super init];
        if (self) {
            _scriptDelegate = scriptDelegate;
        }
        return self;
    }
        
    #pragma mark - WKScriptMessageHandler
        //遵循WKScriptMessageHandler协议,必须实现如下方法,然后把方法向外传递
        //通过接收JS传出消息的name进行捕捉的回调方法
    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        
        if ([self.scriptDelegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
            [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
        }
    }
        
    @end
    
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    WKUserContentController *wkUController = [[WKUserContentController alloc] init];
    //以下代码适配文本大小,由UIWebView换为WKWebView后,会发现字体小了很多,这应该是WKWebView与html的兼容问题,解决办法是修改原网页,要么我们手动注入JS
    NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
    //用于进行JavaScript注入
    WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    
    [wkUController addUserScript:wkUScript];
    // 创建一个弱引用的 WeakWebViewScriptMessageDelegate 防止循环引用
    WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] init];
    
    for (NSString *methodName in jsCallOCMethodNames) {
         // 添加方法名监听 (主要是这步)
         [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:methodName];
    }
    config.userContentController = wkUController;
    //初始化
     _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config];
    // UI代理
    _webView.UIDelegate = self;
    // 导航代理
    _webView.navigationDelegate = self;
    // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
    _webView.allowsBackForwardNavigationGestures = YES;
    //可返回的页面列表, 存储已打开过的网页 
    //  WKBackForwardList * backForwardList = [_webView backForwardList];
    

    核心代码是 [config.userContentController addScriptMessageHandler:self name:methodName] 这样就添加了一个 JS 调用 OC 的方法监听,当 JS 调用方法时,通过WKScriptMessageHandler 代理方法来获取 JS 调用 OC 的方法的处理

    JS 主动调用 OC 的方法的处理
    OC 代码

    处理添加监听的方法名,即 JS 主动调用 OC 的方法

    #pragma mark - WKScriptMessageHandler
    - (void)userContentController:(WKUserContentController *)userContentController
          didReceiveScriptMessage:(WKScriptMessage *)message {
        // message.name  添加监听的方法名
        if ([message.name isEqualToString: @"JSCallOCMethod1"]) {
            //  message.body   是 `js` 传递的参数 ,一般是 json 字符串
            NSLog(@"MessageBody: %@", message.body);
        }
    }
    
    JS 代码

    核心代码
    window.webkit.messageHandlers.<添加监听的方法名>.postMessage(参数);
    如何不需要参数 postMessage(' ') 这样处理

    window.webkit.messageHandlers.JSCallOCMethod1.postMessage({"key1":"value1","key2":"value2"});
    
    OC 主动调用 JS 的方法的处理

    // OC 调用 JS 方法 并传参数 jsonString, 当不需要参数时,可以 ('') 这样处理

    [self.webView evaluateJavaScript: @"OCCallJSMethod('jsonString')"
               completionHandler:^(id response, NSError * error) {
            NSLog(@"response: %@, \nerror: %@", response, error);
     }];
    
    当 JS 需要调用 OC 带有参数和返回值的方法时
    JS 代码

    1、代用参数需要返回值的方法

    let jsonString = window.prompt("jsCallOCReturnJsonStringMethod","参数jsonString");
    //  jsonString  就是 OC 返回的 jsonString 
    

    2、没有参数需要返回值的方法

    let jsonString = window.prompt("jsCallOCReturnJsonStringMethod");
    //  jsonString  就是 OC 返回的 jsonString 
    
    OC 代码

    这种方法不需要注册监听,是使用 UIDelegate 方法

    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
        if (prompt) {
            // defaultText  是JS 传的JsonString参数
            if (defaultText.length > 0) {
                  // 说明有参数
            }
            if ([prompt isEqualToString:@"jsCallOCReturnJsonStringMethod"]) {
                completionHandler(@"返回的结果,可以自定义");
            }
    }
    

    webview 设置 userAgent

       WKWebView *webView = [WKWebView new];
           [webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable oldAgent, NSError * _Nullable error) {
               if (![oldAgent isKindOfClass:[NSString class]]) {
                   // 为了避免没有获取到oldAgent,所以设置一个默认的userAgent
                   // Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
                   oldAgent = [NSString stringWithFormat:@"Mozilla/5.0 (%@; CPU iPhone OS %@ like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E216", [[UIDevice currentDevice] model], [[[UIDevice currentDevice] systemVersion] stringByReplacingOccurrencesOfString:@"." withString:@"_"]];
               }
               
               NSString *userAgentSuffix = @"自己想要拼接的标识";
               //自定义user-agent
               if (![oldAgent hasSuffix:userAgentSuffix]) {
                   NSString *newAgent =  [oldAgent stringByAppendingFormat:@" %@",userAgentSuffix];;
                   [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent":newAgent}];
                   [[NSUserDefaults standardUserDefaults] synchronize];
                   // 一定要设置customUserAgent,否则执行navigator.userAgent拿不到oldAgent
                   webView.customUserAgent = newAgent;
               }
           }];
    
    HTML 测试文件源码
    <!DOCTYPE html>
    <html>
    <header>
        
    <title id = "title">Title of this page</title>
    
    <!--样式从File.css文件中获取-->
    <link rel="stylesheet" type="text/css" href="File.css">
    <!--样式改变适应手机屏幕大小-->
    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <!--响应的方法从File.js文件中获取-->
    <!--<script type="text/javascript" src="File.js"></script>-->
    
    </header>
    
    <body>
        // 防止 汉字在 APP 端乱码
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <!--    标签,css中设置样式时,会根据id来设置-->
        <p id = "wql">This is my first try to write Html5 file.</p>
        <br/>
        <!--    加粗的文本,css中设置样式时,会根据id来设置-->
        <b id = "myp">This text is bold</b>
    
        <!--    换行符-->
        <br/><br/><br/><br/>
        
        <!--    该按钮的目的是:点击后触发OC的方法-->
        <button type="button" onclick = "JSCallOCMethod1()">JSCallOCMethod1</button>
        <br/><br/>
        
        <button type="button" onclick = "JSCallOCMethod2()">JSCallOCMethod2</button>
        <br/><br/>
        
        <button type="button" onclick = "jsCallOCReturnMethod()">jsCallOCReturnMethod </button>
    
        <!--        定义各个方法-->
        <script type="text/javascript">
            
        // js 主动调用 oc  方法  ,function JSCallOCMethod1 这个名字无关,这个是JS 绑定的
        function JSCallOCMethod1(){
            //  JSCallOCMethod1 是 oc 注册监听的方法
    window.webkit.messageHandlers.JSCallOCMethod1.postMessage({"key1":"value1","key2":"value2"});
        }
    
        // js 主动调用 oc  方法  ,function JSCallOCMethod2 这个名字无关,这个是JS 绑定的
        function JSCallOCMethod2(){
           //  JSCallOCMethod2 是 oc 注册监听的方法window.webkit.messageHandlers.JSCallOCMethod2.postMessage({"key1":"value1","key2":"value2"});
    
        }
        
        //OC 主动调用的方法  接收从OC传过来的值,需要OC调用该方法,并传入值
        function OCCallJSMethod(jsonString){
            alert('js获取到的值'+'OCCallJSMethod:'+jsonString);
        }
    
        //OC 主动调用的方法   jsCallOCReturnJsonStringMethod 这个才是与 OC 交互的方法
        function jsCallOCReturnMethod(){
            //  js 获取 OC 接口返回值  有参数  123123123
            let jsonString = window.prompt("jsCallOCReturnJsonStringMethod","123123123");
    
            //  js 获取 OC 接口返回值  无参数 
            // let jsonString = window.prompt("jsCallOCReturnJsonStringMethod");
            alert('js获取到的值:'+'jsCallOCReturnJsonStringMethod:'+jsonString);
        }
    
        </script>
        
    </body>
    </html>
    
    

    相关文章

      网友评论

          本文标题:iOS开发技巧之:WKWebView 与 JS 交互和基本使用

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