美文网首页ios 知识小集技术牛叉的demo
iOS开发--iOS与JS交互的几种方式

iOS开发--iOS与JS交互的几种方式

作者: dibadalu | 来源:发表于2016-08-28 00:29 被阅读5314次

    Demos

    ObjcAndJSDemo1
    ObjcAndJSDemo2

    iOS与JS交互的几种方式

    • JavaScriptCore:iOS7之后出现的,学习成本不高,是适配iOS7的首选。
    • 拦截协议:拦截协议需要双方共同协商为协议规定一套准则,在交互中要遵循该准则。拦截协议不需要引入任何框架,适合多个平台使用。协议可以如此定义:schemes://model/action?{参数1}={数值1}&{参数2}={数值2}&...
    • 第三方框架WebViewJavaScriptBridge:基于拦截协议进行的封装,学习成本相对JavaScriptCore较高,使用不如JavaScriptCore方便。
    • WKWebView:iOS8之后出现的。

    iOS7之前,Objective-C调用JavaScript代码

    iOS7以前,Objective-C调用JavaScript的方式只有一种,就是通过UIWebView对象的stringByEvaluatingJavaScriptFromString:方法。

    • stringByEvaluatingJavaScriptFromString:方法只能在主线程执行
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
            [self.webView stringByEvaluatingJavaScriptFromString:@"var javascript = 1 + 2"];
    });  
    
    • 通过stringByEvaluatingJavaScriptFromString:方法可以简单地调用系统提供的JavaScript方法
    - (void)webViewDidFinishLoad:(UIWebView *)webView{
        NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
        NSLog(@"%@", title);
    }
    

    iOS7之前,JavaScript调用Objective-C

    • URL请求拦截。

    在Objective-C代码里设置UIWebViewDelegate代理,实现代理方法

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
    

    解释:该方法可以监听到UIWebView中发出的URL请求,通过与H5协商一个URL通信协议,来拦截指定的URL,做相应的操作,并阻止此链接的跳转。

    例子:
    html代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div style="margin-top: 10px">
            <input type="button" value="callPhone" onclick="callPhone()">
        </div>
    </body>
    
    <script>
        // 声明一个名为callPhone的js函数,其会发出一个链接为nativejs://callPhone的请求
        function callPhone() {
            window.location.href = 'nativejs://callPhone';
        }
    
    </script>
    
    </html>
    
    

    objc代码

    /**
     *  在一个网页开始加载一个frame前被调用
     */
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
        NSLog(@"shouldStartLoadWithRequest-------");
        
        NSString *urlString = request.URL.absoluteString;
        NSRange range = [urlString rangeOfString:@"nativejs://"];
        if (range.location != NSNotFound) { // 拦截URL协议头是nativejs的链接
            NSLog(@"执行原生调用相机的方法");
            return NO;// 阻止此链接的跳转
        }
        return YES;
    }
    
    • 监听Cookie。

    详见参考链接的原生与H5的交互一文。

    iOS7之后,JavaScriptCore的引入,使得Objective-C与JavaScript的交互更为容易

    JavaScriptCore中常见的几种类型

    • JSContext:代表JS的执行环境,通过evaluateScript:方法就可以执行JS方法。
    • JSValue:封装了JS与ObjC中对应的模型,以及调用JS的API等。
    • JSExport:一个协议,通过遵守此协议,可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,能被JS调用。
    • JSManagedValue:管理数据和方法的类。
    • JSVirtualMachine:处理线程相关,使用较少。

    Objective-C调用JavaScript

    /**
     *  网页加载完毕时被调用
     */
    - (void)webViewDidFinishLoad:(UIWebView *)webView{
        // 获取当前JS执行环境
        JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        NSString *alertJS = @"alert('Hello JS!')"; //准备执行的JS代码
        // 通过evaluateScript:方法调用JS的alert
        [context evaluateScript:alertJS];
    }
    

    JavaScript 调用 Objective-C

    • 直接调用JS(不适用于实战项目中)。

      例子1:
      objc代码

      // 获取当前JS执行环境
        JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        
        // context直接执行JS代码。
        [context evaluateScript:@"var num = 10"];
        [context evaluateScript:@"var squareFunc = function(value) { return value * value }"];
        // 计算正方形的面积
        JSValue *squareArea = [context evaluateScript:@"squareFunc(num)"];
        NSLog(@"squareArea:%@", squareArea.toNumber);
        
        // 也可以通过下标的方式获取到JS函数
        JSValue *squareFunc = context[@"squareFunc"];
        JSValue *squareArea2 = [squareFunc callWithArguments:@[@"20"]];
        NSLog(@"squareArea2:%@", squareArea2.toNumber);
      

      例子2:
      html代码

      <div style="margin-top: 10px">
          <input type="button" value="log" onclick="log('测试')">
      </div>
      

      objc代码

    /**

    • 网页加载完毕时被调用
      */
    • (void)webViewDidFinishLoad:(UIWebView *)webView{
      // 获取当前JS执行环境
      JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
      // 直接调用JS代码
      context[@"log"] = ^(){
      // 取出JS方法的参数
      NSArray *args = [JSContext currentArguments];
      for (id obj in args) {
      NSLog(@"%@",obj); // 打印JS方法接收到的所有参数
      }
      };
      }

      
      
    • 在ObjC中通过JSContext注入模型,然后调用模型的方法。(重要,项目一般用该方式)

    第一步:需要声明一个与JS进行交互的协议(NativeApisProtocol),要求该协议遵守JSExport协议。
    第二步:新建一个模型(NativeAPIs),要求该模型遵守NativeApisProtocol协议。一般而言,需要在NativeAPIs模型中声明一个JSContext属性,便于与JS交互。
    第三步:实现该模型(NativeAPIs),即在NativeAPIs.m文件中实现NativeApisProtocol协议中定义的方法。
    第四步:在ViewController.m的-webViewDidFinishLoad:方法中获取当前JS执行环境(self.jsContext),然后将NativeAPIs模型注入到JS执行环境。

    注意:NativeApisProtocol协议中定义的方法是在子线程中执行的,如果在所定义的方法中需要修改界面或者跳转之类的,需要通过GCD回主线程操作。

    缺陷:通过参考链接的JavaScript和Objective-C交互的那些事(续)可知,在-webViewDidFinishLoad:方法中注入NativeAPIs模型存在一定的问题,因为这时候网页还没加载完,JavaScript若开始调用Objective-C代码(即NativeApisProtocol协议中定义的方法),会出现调用不到方法的问题。
    解决方法:在每次创建JSContext环境的时候,都注入NativeAPIs模型到JSContext环境中。更加具体的方法可以参考第三方库UIWebView-TS_JavaScriptContext。通过引入UIWebView+TS_JavaScriptContext,让ViewController遵守TSWebViewDelegate协议,实现代理协议中的方法-webView:didCreateJavaScriptContext:,以此获取JSContext环境。

    objc代码

    /*--  NativeAPIs.h  ---*/ 
    
    #import <Foundation/Foundation.h>
    #import <JavaScriptCore/JavaScriptCore.h>
    #import <UIKit/UIKit.h>
    
    // 声明与JS交互的协议
    @protocol NativeApisProtocol <JSExport> // 遵守JSExport协议
    
    // 调用系统相机
    - (void)callCamera;
    
    // 调用系统分享
    - (void)share:(NSString *)shareInfo;
    
    // 打开宁波手机阅读
    - (void)openNBPhoneReader;
    
    @end
    
    @interface NativeAPIs : NSObject <NativeApisProtocol>
    
    @property(weak, nonatomic) JSContext *jsContext;
    
    @end
    
    /*--------------------------------------------*/
    
    /*--  NativeAPIs.m --*/ 
    
    /*--  省略,具体看Demo源码 --*/
    
    /*--------------------------------------------*/
    
    // ViewController.m
    
    /*-- 省略前面的代码 --*/
    
    #pragma mark - UIWebViewDelegate
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView{
        NSLog(@"网页加载完毕------");
        
        // 获取JS上下文运行环境
        self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        NativeAPIs *nativeAPIs = [[NativeAPIs alloc] init];
        // 将NativeAPIs模型注入JS
        self.jsContext[@"NativeApis"] = nativeAPIs;
        nativeAPIs.jsContext = self.jsContext;
        
        self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
            context.exception = exceptionValue;
            NSLog(@"异常信息: %@", exceptionValue);
        };
    }
    
    

    html代码

    <html>
    
    /*-- 省略无关紧要的代码,具体见Demo --*/
    
    <body>
        <div style="margin-top: 10px">
            <input type="button" value="CallCamera" onclick="NativeApis.callCamera()">
        </div>
        <div style="margin-top: 50px">
            <input type="button" value="Share" onclick="callShare()">
        </div>
        <div style="margin-top: 50px">
            <input type="button" value="OpenReader" onclick="NativeApis.openNBPhoneReader()">
        </div>
    </body>
    
    <script>
        // 声明一个名为picCallback的函数,其参数为photo
        var picCallback =  function (photo) {
            alert(photo);
        }
    
        // 声明一个名为callShare的函数
        var callShare = function () {
            var shareInfo = JSON.stringify(
                                          { "title": "objc&js的交互",
                                            "desc": "就是那些事"}
                                          );
            // 调用原生的share方法
            NativeApis.share(shareInfo);
        }
    
        // 声明一个名为shareCallback的函数
        var shareCallback =  function () {
            alert('success');
        }
    
    </script>
    
    </html>
    
    

    参考链接

    iOS与JS交互实战篇(ObjC版)

    UIWebView 与 JS 交互(1):Objective-C 调用 Javascript

    原生与H5的交互

    Objective-C与JavaScript交互的那些事

    JavaScript和Objective-C交互的那些事(续)

    JavaScriptCore在实际项目中的使用的坑

    UIWebView中Objective-C与JavaScript交互

    UIWebView加载本地HTML文件

    OC与JS的交互

    iOS中JavaScript和OC交互

    UIWebView-TS_JavaScriptContext

    关于UIWebView的总结

    UIWebview 的javascript與ios objective-c互動傳參數

    UIWebview 的javascript與ios objective-c互動傳參數(Ⅱ)

    js(javascript)与OC(Objective-C)交互

    iOS中的HTML交互简说 -- 推荐

    相关文章

      网友评论

        本文标题:iOS开发--iOS与JS交互的几种方式

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