年年有"余"

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2339|回复: 3

[项目经验] Object-C与JavaScript交互的两种方式

[复制链接]
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

    发表于 2016-3-17 17:06:20 | 显示全部楼层 |阅读模式
    本帖最后由 Sian 于 2016-3-17 17:30 编辑

    1、在iOS项目开发中,原生页面中掺杂Html5页面已不是什么新鲜事了,毕竟H5也他相关的优势,比如布局、富文本内容展示等;

    2、随着H5页面的侵入,OC与JS的交互意愿越来越强烈,就目前来看,在不引用第三方库的前提下,有两种方式可以解决;
    2.1、iOS6及以前通过拦截NSRequest请求来调用原生方法进行交互;
    2.2、iOS7及以后的版本苹果官方引入了JavaScriptCore框架;

    3、拦截NSRequest请求
    A、UIwebView有个代理方法:
    [Objective-C] 纯文本查看 复制代码
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
    B、此方法通过返回YES/NO来决定是否跳转到request的页面,因此我们可以通过协议头来区分是正常的URL请求还是本地方法的调用请求;
    C、比如说网页中某a标签请求的地址是 saap://share,那么在该代理方法中就可以通过url的协议头来将其拦截,返回NO,页面不跳转,同时执行某个share方法,即达到了调用OC本地方法的效果;
    D、参数可以通过url中带过来,或者参数内容过长可以通过post请求,在request的HTTPBody中可以将post请求内容解析出来
    E、现在调OC方法没问题了,OC如何调用页面中的方法呢?其实UIWebView给我们也提供了一个交互方法
    [Objective-C] 纯文本查看 复制代码
    - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
    直接传入JS脚本,还可以接收脚本的返回值

    4、JavaScriptCore框架
    4.1、在iOS7及以后的版本中可以通过官方提供的JS框架来进行交互,主要涉及到的两个类是JSValue、JSContext
    4.2、JSContext指的是JS的上下文,即可以简单解理成<script></script>标签部分;
    4.3、JSValue是一个在OC和JS之间进行数据转换的类,他可以将JS对象或数据转换成OC的对象或数据,反之亦然;
    4.4、如何使用呢,又分为两种方式,一种方式通过Block(即所谓的匿名函数),另一种方法即通过代理,这是苹果的一惯作风!

    5、两种方法的对比
    5.1、Block的基本实现方式:
    A、在UIWebView的代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView加入以下代码
    [Objective-C] 纯文本查看 复制代码
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        context[@"ocShow"] = ^(){
            NSLog(@"来自JS的问候...");
        };
        // 模拟JS调用
        [context evaluateScript:@"ocShow()"];
    }
    网页中如果JS调用ocShow()方法,即会输入这行"来自JS的问候...";
    [Objective-C] 纯文本查看 复制代码
    2016-03-17 17:04:34.937 SAJavaScript[8339:233216] 来自JS的问候...
    B、需要传参数怎么办?两种方法
    C、一种方法是在这个Block中添加参数:^(NSString *str1, NSString *str2){},如果对Block不熟悉可以参考http://www.yusian.com/thread-976-1-1.html 或者网上找下相关资料看看先;
    D、另外一种方法,通过JSContext的类方法currentArguments可以获取NSArray *argument = [JSContext currentArguments]; 然后遍历获得;
    E、这种方法使用简单方便,但每次都要重新写,可不可以指向我已有的现成方法?当然是可以的!使用代理
    5.2、通过代理实现
    A、自定义一个协议,继承自<JSExport>,把JS需要调用的方法在该协议中申明;
    B、但凡遵循<JSExport>协议的对象都可以被JS所调用
    C、实现相关方法即可
    [Objective-C] 纯文本查看 复制代码
    //
    //  ViewController.m
    //  SAJavaScript
    //
    //  Created by 余西安 on 16/3/17.
    //  Copyright &#169; 2016年 Sian. All rights reserved.
    //
    
    #import "ViewController.h"
    #import <JavaScriptCore/JavaScriptCore.h>
    
    @protocol SAJSExport <JSExport>
    - (void)showMessage:(JSValue *)message;
    @end
    
    @interface ViewController () <UIWebViewDelegate, SAJSExport>
    @property (nonatomic, strong) UIWebView *webView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        self.webView = [[UIWebView alloc] init];
        self.webView.delegate = self;
        self.webView.frame = self.view.bounds;
        [self.view addSubview:self.webView];
        NSURL *url = [NSURL URLWithString:@"http://www.yusian.com"];
        [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
    }
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        // 1、Block方法
        context[@"ocShow"] = ^(){
            NSLog(@"来自JS的问候...");
        };
        // 模拟JS调用
        [context evaluateScript:@"ocShow()"];
        
        // 2、代理方法
        [context setObject:self forKeyedSubscript:@"webView"];
        // 模拟JS调用webView.showMessage()方法
        [context evaluateScript:@"webView.showMessage('Hello JavaScript')"];
    }
    
    - (void)showMessage:(JSValue *)message
    {
        [[[UIAlertView alloc] initWithTitle:nil message:[message toString] delegate:nil cancelButtonTitle:@"朕知道了" otherButtonTitles:nil, nil] show];
    }
    @end
    
    D、效果图

    Simulator-Screen-Shot-2016年3月17日-下午5.25.36.jpg

    E、回调JS就更简单了,其实上面已经调用过了,JSContext中的方法
    [Objective-C] 纯文本查看 复制代码
    - (JSValue *)evaluateScript:(NSString *)script;
    F、JSValue与OC数据转换对应关系
    [HTML] 纯文本查看 复制代码
    <pre>
    @textblock
       Objective-C type  |   JavaScript type
     --------------------+---------------------
             nil         |     undefined
            NSNull       |        null
           NSString      |       string
           NSNumber      |   number, boolean
         NSDictionary    |   Object object
           NSArray       |    Array object
            NSDate       |     Date object
           NSBlock (1)   |   Function object (1)
              id (2)     |   Wrapper object (2)
            Class (3)    | Constructor object (3)
    @/textblock
    </pre>
    6、Demo下载
    游客,如果您要查看本帖隐藏内容请回复


    该用户从未签到

    发表于 2016-3-22 10:14:05 | 显示全部楼层
    求Demo学习。。:P

    该用户从未签到

    发表于 2016-3-27 22:38:56 | 显示全部楼层
    来学习了:):):):):),就想看看js端的代码是怎么写的。
  • TA的每日心情
    开心
    2016-4-2 10:09
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2016-4-2 09:47:35 | 显示全部楼层
    感谢楼主分享。。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|小黑屋|Archiver|iOS开发笔记 ( 湘ICP备14010846号 )

    GMT+8, 2024-4-20 09:34 , Processed in 0.049955 second(s), 21 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表