Objective-C与JavaScript交互的那些事

作者: TIME_for | 来源:发表于2016-01-16 22:48 被阅读25782次

    注:此文只现在只推荐需要适配iOS7的同学读,如果已经扔掉iOS7,强烈建议换用WKWebView。已出WKWebView文章WKWebView使用及注意点(keng)

    最近公司的运营瞎搞了个活动,其活动要服务端提供数据支持,web前端在微信公众账号内作为主要的运营阵地,而iOSAndroid要提供相应的入口及页面进行配合。一个活动,动用了各个端的程序猿。而在这里面技术方面主要就是涉及到web端和服务端的交互,web前端iOSAndroid的交互。本人作为一个iOS开发者,今天就聊聊webiOSAndroid三端的交互,其实在说明白一点就是方法的互相调用而已。这里主要讲解iOSAndroid会稍微提一下,仅作参考。

    此篇文章的逻辑图

    图0-0 此篇文章的逻辑图

    概述

    iOS原生应用和web页面的交互大致上有这几种方法iOS7之后的JavaScriptCore拦截协议第三方框架WebViewJavaScriptBridgeiOS8之后的WKWebView在这里主要讲解JavaScriptCore拦截协议这两种办法。WebViewJavaScriptBridge是基于拦截协议进行的封装。学习成本相对JavaScriptCore较高,使用也不如JavaScriptCore方便本文不做叙述。WKWebView是iOS8之后推出的,还没有成为主流使用,所以本篇文章也不做详细叙述。

    Objective-C执行JavaScript代码

    相关方法

    // UIWebView的方法
    - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
    
    // JavaScriptCore中JSContext的方法
    - (JSValue *)evaluateScript:(NSString *)script;
    - (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL *)sourceURL
    

    相关应用

    用这些方法去执行大段的JavaScript代码是没什么必要的,但是有些小场景用起来还是比较顺手和实用的,列举两个例子作为参考:

    // 获取当前页面的title
    NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.title"];
    
    // 获取当前页面的url
    NSString *url = [webview stringByEvaluatingJavaScriptFromString:@"document.location.href"];
    

    JavaScriptCore

    iOS7之后苹果推出了JavaScriptCore这个框架,从而让web页面和本地原生应用交互起来非常方便,而且使用此框架可以做到Android那边和iOS相对统一,web前端写一套代码就可以适配客户端的两个平台,从而减少了web前端的工作量。

    web前端

    在三端交互中,web前端要强势一些,一切传值、方法命名都按web前端开发人员来定义,让另外两端去做适配。在这里以调用摄像头和分享为例来详细讲解,测试网页代码取名为test.html,其代码内容如下:

    test.html代码内容
    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
    </head>
    <body>
        <div style="margin-top: 100px">
            <h1>Objective-C和JavaScript交互的那些事</h1>
            <input type="button" value="CallCamera" onclick="Toyun.callCamera()">
        </div>       
            
        <div>
            <input type="button" value="Share" onclick="callShare()">
        </div>
    
    <script>
        var callShare = function() {
            var shareInfo = JSON.stringify({"title": "标题", "desc": "内容", "shareUrl": "http://www.jianshu.com/p/f896d73c670a",
            "shareIco":"https://img.haomeiwen.com/i1192353/fd26211d54aea8a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"});
            Toyun.share(shareInfo);
        }
    
        var picCallback = function(photos) {
            alert(photos);
        }
    
        var shareCallback = function(){
            alert('success');
        }
    </script>
    </body>
    </html>
    
    test.html代码解释

    可能有些同学对web前端的一些知识不太熟悉,稍微对这段代码做下解释,先说ToyuniOSAndroid这两边在本地要注入的一个对象【参考下面iOS的代码更容易明白】,充当原生应用和web页面之间的一个桥梁。页面上定义了两个按钮名字分别为CallCameraShare。点击CallCamera会通过Toyun这个桥梁调用本地应用的方法- (void)callCamera,没有传参;而点击Share会先调用本文件中的JavaScript方法callShare这里将要分享的内容格式转成JSON字符串格式(这样做是为了适配AndroidiOS可以直接接受JSON对象)然后再通过Toyun这个桥梁去调用原生应用的- (void)share:(NSString *)shareInfo方法这个是有传参的,参数为shareInfo。而下面的两个方法为原生方法调用后的回调方法,其中picCallback为获取图片成功的回调方法,并且传回拿到的图片photosshareCallback为分享成功的回调方法。

    iOS

    iOS这边根据前端定义的方法名来写代码,但是有些时候web前端会让我们定义,但是我们定义好之后他又要修改,这时候就会很烦啊。所以碰到三端交互的时候最好就是让web前端去定义方法名,iOSAndroid根据web前端定义好的去写代码。JavaScriptCoreweb页面调用原生应用的方法可以用DelegateBlock两种方法,此文以按Delegate讲解。

    JavaScriptCore中类及协议:
    • JSContext:给JavaScript提供运行的上下文环境
    • JSValue:JavaScriptObjective-C数据和方法的桥梁
    • JSManagedValue:管理数据和方法的类
    • JSVirtualMachine:处理线程相关,使用较少
    • JSExport:这是一个协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议
    ViewController中的代码
    #import "ViewController.h"
    #import <JavaScriptCore/JavaScriptCore.h>
    
    @protocol JSObjcDelegate <JSExport>
    
    - (void)callCamera;
    - (void)share:(NSString *)shareString;
    
    @end
    
    @interface ViewController () <UIWebViewDelegate, JSObjcDelegate>
    
    @property (nonatomic, strong) JSContext *jsContext;
    @property (weak, nonatomic) IBOutlet UIWebView *webView;
    
    @end
    
    @implementation ViewController
    
    #pragma mark - Life Circle
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
        [self.webView loadRequest:[[NSURLRequest alloc] initWithURL:url]];
        
    }
    
    #pragma mark - UIWebViewDelegate
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        self.jsContext[@"Toyun"] = self;
        self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
            context.exception = exceptionValue;
            NSLog(@"异常信息:%@", exceptionValue);
        };
    }
    
    #pragma mark - JSObjcDelegate
    
    - (void)callCamera {
        NSLog(@"callCamera");
        // 获取到照片之后在回调js的方法picCallback把图片传出去
        JSValue *picCallback = self.jsContext[@"picCallback"];
        [picCallback callWithArguments:@[@"photos"]];
    }
    
    - (void)share:(NSString *)shareString {
        NSLog(@"share:%@", shareString);
        // 分享成功回调js的方法shareCallback
        JSValue *shareCallback = self.jsContext[@"shareCallback"];
        [shareCallback callWithArguments:nil];
    }
    
    @end
    
    ViewController中的代码解释

    自定义JSObjcDelegate协议,而且此协议必须遵守JSExport这个协议,自定义协议中的方法就是暴露给web页面的方法。在webView加载完毕的时候获取JavaScript运行的上下文环境,然后再注入桥梁对象名为Toyun,承载的对象为self即为此控制器,控制器遵守此自定义协议实现协议中对应的方法。在JavaScript调用完本地应用的方法做完相对应的事情之后,又回调了JavaScript中对应的方法,从而实现了web页面本地应用之间的通讯。

    JavaScriptCore使用注意

    JavaScript调用本地方法是在子线程中执行的,这里要根据实际情况考虑线程之间的切换,而在回调JavaScript方法的时候最好是在刚开始调用此方法的线程中去执行那段JavaScript方法的代码,我在实际运用中开始没注意,就被坑惨了啊。什么,说的太绕,看下面的代码解释:

    //  假设此方法是在子线程中执行的,线程名sub-thread
    - (void)callCamera {     
        // 这句假设要在主线程中执行,线程名main-thread
        NSLog(@"callCamera");  
    
        // 下面这两句代码最好还是要在子线程sub-thread中执行啊
        JSValue *picCallback = self.jsContext[@"picCallback"];
        [picCallback callWithArguments:@[@"photos"]];
    }
    
    运行效果

    运行效果如图3-1所示

    图3-1 运行效果

    拦截协议

    拦截协议这个适合一些比较简单的一些情况,不需要引入什么框架,只需要web前端配合一下就好。但是在具体调用哪一个方法上,以及在传值的时候可能会有些不方便,而且调用完后无法在回调JavaScript的方法。

    web前端

    test.html中的代码
    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
    </head>
    <body>
        <div>
            <input type="button" value="CallCamera" onclick="callCamera()">
        </div>
    
    <script>
        function callCamera() {
            window.location.href = 'toyun://callCamera';
        }
    </script>
    </body>
    </html>
    
    test.html中的代码解释

    这段代码相比上面的那段测试代码是很简单的,同样有一个按钮,名字为CallCamera点击之后调用自己的callCamera方法,window.location.href这里是改变主窗口的指向从而马上发出一个链接为toyun://callCamera请求,而想要传给原生应用的参数也可已包含到此请求中,而在iOS方法中我们要拦截这个请求,根据请求内容去判断JavaScript想要做的事情,从而实现web页面本地应用之间的交互。

    iOS

    iOS对应的代码
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    {
        NSString *url = request.URL.absoluteString;
        if ([url rangeOfString:@"toyun://"].location != NSNotFound) { 
            // url的协议头是toyun
            NSLog(@"callCamera");
            return NO;
        }
        return YES;
    }
    
    iOS对应的代码的解释

    webView的代理方法中去拦截自定义的协议Toyun://如果是此协议则据此判断JavaScript想要做的事情,调用原生应用的方法,这些都是提前约定好的,同时阻止此链接的跳转。

    总结

    随着手机硬件的配置越来越强大和HTML5的兴起,一个App完全可以由web页面来写。现在已经有部分应用这么干了,我是遇见过的,如古诗文网。尽管比较少但是web页面本地应用的交互不论是iOS还是Android都是会有遇到的。iOS我还是比较推荐JavaScriptCore,这样三端可以相对统一起来,写的时候都比较简单。随着时间的推移iOS8推出的WKWebView会逐渐成为主流,这个的功能更强大。拦截协议也只能说用到比较简单的一些情况吧,复杂的情况处理相互之间参数的传递还是比较麻烦的,而且这个不能回调JavaScript的方法,确实喜欢拦截协议的同学可以研究WebViewJavaScriptBridge这个第三方库。对于Android本人也就是略知皮毛而已,就不班门弄斧了,对于一些Android开发者来说,可以看地第一段的test.html这个页面的写法完全是可以适配Android的。

    更新

    关于使用过程中的坑,出了一片续,具体参看JavaScript和Objective-C交互的那些事(续)

    关于WKWebView,已经出了一篇新文章,具体参看WKWebView使用及注意点(keng)

    参考

    相关文章

      网友评论

      • 笨笨编程:@老亮 为什么服务端在html 里面定义Toyun 的时候直接JS报错~然而在xcode 里面加载静态html 是不会有问题的
      • fedfcb0e1e61:拦截协议也只能说用到比较简单的一些情况吧,复杂的情况处理相互之间参数的传递还是比较麻烦的,而且这个不能回调JavaScript的方法

        关于这个其实也是支持的,只需要把回调的js方法传过来就行,等OC代码执行完调用stringByEvaluatingJavaScriptFromString回调回去就行
      • liangdahong:666 博主、你好,我想请问一下,我们在弄 OC 和 JS 交互时,参数中可能会出现 '=‘ '&' 等特殊字符,那么我们在用 & 或者 = 取参数时可能就有一些问题,有什么好的方法处理嘛?准备用 base64对数据进行处理,但是又和有 '=' 号。:stuck_out_tongue_closed_eyes:
      • 56d35fdf1a2a:我在工程中test.html可以实现交互 换成公司服务端url就不能交互了 js代码都是一样的 这是什么原因呢
        TIME_for:@来自蒙塔基的罡蛋 Callback 函数在 JS 那边要是一个全局的函数才可以这样调用。
        56d35fdf1a2a:@TIME_for 我在工程中获取的设备信息的数据str 进行交互 在- (void)webViewDidFinishLoad:(UIWebView *)webView中
        NSString *js = [NSString stringWithFormat:@"Callback(\"%@\")", str];
        [context evaluateScript:js];
        服务端url那里有Callback的函数 只是简单的实现alert(str) 然后就显示不出来
        但是换成这样就可以NSString *js = [NSString stringWithFormat:@"alert(\"%@\")", str];
        这是为什么呢 大神
        TIME_for:是否是因为交互对象注入时机出了问题呢?看一下续文中提到的方法是否能解决
      • 那时风过:[picCallback callWithArguments:@[@"photos"]]; 请问这个photos,参数是直接是图片的123.png这样的名字吗,还是这个图片的本地URL,由于不会JS,不知道JS能接收图片以啥形式
        TIME_for:@那时风过 我们是APP传到网上,然后给前端的URL,如果直接传图片,就要按一定的编码格式,编成二进制文件传给他们。
      • PGOne爱吃饺子:大神 您好 ,请问如果我加载的是一个网页链接 ,self.jsContext 应该如何初始化呢,
        TIME_for:@4140d18ee6fc 这两个时机都不是太好啊。续文中已经给出了最佳的时机。另外不适配iOS7,也建议换WKWebView虽说这个控件坑深,但是瑕不掩瑜啊。
        PGOne爱吃饺子:还有楼组,你的那个self.jsContext初始化的时候最好是在webView开始加载的时候初始化,而不是在那个webView要加载完成的时候初始化。
        TIME_for:@4140d18ee6fc 上面已经给出了,不过建议参考 续文。
      • 多LV信源:写得真好, 有条理
        TIME_for:@多LV信源 UIWebView已弃坑,欢迎进入WKWebView的大坑。
      • Vampire丶Lv:博主你好,我现在是js给oc能传过值去,但是js会报一个异常,导致后续的callback方法不能执行,不知道是怎么回事==、
        异常信息:TypeError: undefined is not an object
        笨笨编程:@Vampire丶Lv 这个是web前段报的错~最后怎么解决的啊
        TIME_for:@Vampire丶Lv 传值类型是否有误?
      • 不寐的星光:恩,写的很实在,易懂易学,不过看来要换WKWebView了
      • Amok校长:有Demo吗
        Amok校长:@TIME_for 嗯,已经搞定了
        TIME_for:@kirs demo没有,核心代码都在文章里面了,可自己看着做一下。
      • Vincent_fs:你好,跳转相机拍照后返回,弹框就失去响应了是怎么回事?
        TIME_for:@普通的凡人 可能是线程的问题?
      • AryCode:涨知识了
        TIME_for:@CarryIT01 共同进步~~
      • BeyondChao:博主你好,我按照你写demo,自己重新写了一遍,结果遇到两个问题:
        1. 点击callCamera的时候报异常:TypeError: undefined is not an object。
        2. 点击share按钮的时候,没有任何返回,也没有log信息。
        请问博主遇到过吗?多线程的原因吗?
        BeyondChao:@TIME_for 就是对象注入失败了,可能是时机不对。
        TIME_for:@BeyondChao 检查一下有没有注入对象,web调用方法的时候,是不是正确。
        BeyondChao:请问博主遇到过类似情况嘛?
      • 那一地流苏:写得非常好,帮我解决了问题,希望再接再厉
        TIME_for:@那一地流苏 共同进步~~,不过现在都已经换用WKWebView,这个有点过时了啊。
      • 梦魇丶:self.jsContext[@"Toyun"] = self;这行代码循环引用,绝对内存泄漏,请教怎么解决
        笨笨编程:@TIME_for 为什么服务端在html 里面定义Toyun 的时候直接JS报错~然而在xcode 里面加载静态html 是不会有问题的
        TIME_for:后来出的续,给出了解决办法。
      • 石头撞地球:逻辑图用什么软件做的?很喜欢
        TIME_for:@不磨人的小妖精 MindLine
      • dibadalu:博主,在objc与js的交互中,如果objc这边提供给js的某个方法已经成功执行,想将执行结果回调给js,应该采用什么样的方式。我看了你的博文,猜测:通过JSValue与self.context 获取js的某个函数,然后在objc调用它,回传结果值给js?这样的方式可行吗?是否有更好的方式?
        TIME_for:@dibadalu UIWebView已经成为过去式,换WKWebView基本用不到这些方法了。
        TIME_for:@dibadalu JS可以直接传过来一个匿名函数,OC这边接收到为JSValue,然后用这个对象直接调用方法 callWithArguments:也可以。省去了从名字寻找JSValue的步骤。
      • 顺其自然JX:大神,,,怎么没有看到实现JSObjcDelegate啊?
      • 3cd50720ca18:想问下博主,就你描述的这种情况下,要是用WKWebView该怎么做?要怎样仿照微信与前端做JS交互,前提是前端代码完全不改。
        四月_明朗:@TIME_for 楼主,可以出一篇WKWebVIew的文章了呢 :kissing_heart:
        TIME_for:@skye11 WKWebView官方给出了明确的交互方式,还没找到能统一的方式,最近项目要扔掉iOS7,这个问题还在看呢?
      • c946e16a6cdc:亲,问下webView加载的html数据。其页面上的控件不都是能响应的么?为什么还要跟本地交互啊???例如webView加载百度页面,点哪里不都是可以响应的么。这交互有啥子明显的用呢?新手,实在想不明白,求教勿喷。谢谢。。。
        四月_明朗:@主要看实力 比如,你要通过web界面上的一个按钮,跳转到一个你的原生的界面。这不是简单的交互么
        TIME_for:@主要看实力 到你们有这个需求的时候,自然就能明白了。
      • YPJin:mark
      • 万少:亲 我直接把你的代码都拷贝下来的 但是我的点击两个按钮啥都没有啊
        TIME_for:@万少 仔细阅读以下,看看是不是哪儿遗漏了?
      • 四月_明朗:通过Toyun这个桥梁,这里这个Toyun是什么啊?没有遇到过,求问
        笨笨编程:@老亮 为什么服务端在html 里面定义Toyun 的时候直接JS报错~然而在xcode 里面加载静态html 是不会有问题的
        四月_明朗:耐心看了所有人的回复。感觉需要学习的东西还有不少
        TIME_for:@老亮 在拦截协议中,Toyun指的是自定义协议,这个自定义就好。Toyun也是我随意起的。在JavaScriptCore中,Toyun是存在于JSContext中,充当交互的桥梁,self.jsContext[@"Toyun"] = self;当然这个名字也是可以随意起的。理解成OC中的代理(当然并不是OC中的代理)可能容易理解一些。
      • 动感超人丶:写的很实在,很容易看得懂,谢谢分享
        TIME_for:@动感超人丶 这个仅仅实现了功能,里面还有部分坑没解决,最近应该会出一片续,来说一下使用的深坑。
      • BestVast:请问js能调oc而oc调js的方法在有的程序里不能用,这个是与什么有冲突吗?
        TIME_for:@风_雨 仔细检查一下代码~~
        BestVast:@Toyun 实在不好意思,刚看到。OC调用JavaScriptCore的那两种方法我都试过,在有的程序里调用,在有的程序里不调用,把我整的发懵了
        TIME_for:@风_雨 没有冲突,是不是代码写的有点问题呢
      • 一个人的阳光:楼主,我也是做iOS,去年用JS月Native交互的时候也用了JavaScriptCore,当时觉得只有几个接口就自己写了,实现方式跟你的一样,你说的线程问题我们也碰到了,解决方案跟你说的方法一样,但是不知道你还有没有遇到过另外的问题, 如: 1、内存泄露的问题 2、打开webView后Native和JS交互正常,但是点击Web上一个按钮,Web端重新启用了一个新的JS环境(刷新出一个新Web界面)(在当前控制器下),交互是否正常 以上两个问题是否遇到,如果遇到是否已经找到解决方案?如果有更好的解决方案请转告一下,谢谢(我QQ:1298301437)
        一个人的阳光:@Toyun 我的问题解决了,我以前使用VC来做实现JSExport的model的,这样会造成循环引用,加了一层model后就可以正常释放了
        TIME_for:@一个人的阳光 1、内存管理,OC用的ARC,JavaScriptCore用的garbage collection,关于此引起的内存泄露问题,JavaScriptCore中提供了JSManagedValue这个类去管理,内存泄露问题不难解决。2、我的使用场景是同一个web页,但是这个web页面会一直自动刷新,交互没有影响,而且JSContext用了懒加载。但是出现了另一个诡异问题(问题也不好描述),也是没有找到原因 。
      • BerrySang:貌似有几处JavaScript写成了JavaStript,不过不影响。
        楼主,看了这个和你写的推送的那两篇,都很不错。
        又不小心点了你的微博,发现是老乡,能加下联系方式吗,有问题的话可以交流交流,我的Q617106391,谢谢了。
        TIME_for:@BerrySang 感谢你看的这么仔细,感谢指正,已经修改~~
      • 比基尼熊仔:如果是多个参数,貌似是响应不了. 从js到oc
        比基尼熊仔:@Toyun 嗯 可以响应,这个方法取名是不是有技巧
        比基尼熊仔:@Toyun 我按照 - (void)jsCallOc:(NSString *)title msg:(NSString *)msg;
        onclick="Toyun.jsCallOcMsg('js title', 'js message')" 但就是没有响应 不知道为啥, 这里多参数的方法,想请教下 sdk如何知道jsCallOcMsg哪个是第2个参数
        TIME_for:@比基尼熊仔 可以的,OC方法- (void)jsCallOc:(NSString *)title msg:(NSString *)msg;JS中button的onclick时间onclick="Toyun.jsCallOcMsg('js title', 'js message')"。响应不了,应该是你JS中方法没有写对。
      • 未来的自己在哪里:简直通熟易懂,谢谢。
        TIME_for:@未来的自己在哪里 共同进步~~
      • 子达如何:还有一个疑问的地方:
        self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        这个是官方建议的获取UIWebView Context的方法吗?
        貌似没有找到任何官方文档说明这个KeyPath的用法,这样用算不算是使用了私有API了?
        TIME_for:@子达如何 这个倒不至于,https://developer.apple.com/videos/play/wwdc2013-615/这是2013年的WWDC专门讲JavaScriptCore的一段视频。不推荐,开发者大会上不会浪费这么多时间的吧。
        子达如何:@Toyun 找了一圈都没有发现官方给出了方法可以访问uiwebview的jscontext,是不是其实Apple并不推荐我们使用javascriptcore与uiwebview交互的意思?
        TIME_for:@子达如何 这个问题问的好,我先粗略的解释一下。这个方法调用的API是 valueForKeyPath: 这个根据运行时去动态获取一些值,和 setValue: forKeyPath: 这个根据运行时去动态设置一些值并不算是私有API,只是苹果不太希望你这样做,去改他们的一些东西。然后在说 [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]这行代码,官方确实没有说这样用,我也是根据别人博客参考而得。但是过审核没有问题的,这个已经实践过,可放心使用。
      • 黑夜漫步:昨天刚看了参考的那个博客,还下了个源码,相比之下,感觉楼主的比较精简易懂些
        TIME_for:@黑夜漫步 那个博客考虑到了解耦,和内存管理的东西了。
      • 子达如何:[而且调用完后无法在回调JavaScript的方法] 这个不是很认同呢
        WebViewJavaScriptBridge用了一个比较巧妙的方法实现了OC和JS之间的双向回调:
        在OC和JS端分别保存了一个回调函数的字典,在处理请求的时候首先检查回调函数的字典,如果有,就是一个回调。
        子达如何:@Toyun 言重了,没有不便,大家交流交流😄
        TIME_for:@子达如何 这个我仅仅指的是下面那个简单的拦截协议。WebViewJavaScriptBridge这个在其拦截协议基础上进行了封装。并没有说WebViewJavaScriptBridge这个不能实现回调。可能是我表达的不太清楚,给你带来不便,敬请谅解。回头我这边把这点更新一下。再次感谢指正错误~~
      • 小酒窝_David:没加JSObjcDelegate
      • 小酒窝_David:遇到了一个问题,我按照你写的,写了一个demo,但是店家test.html上面的按钮的时候,没有调用viewcontroller的方法,我的环境是xcode7.2,手机iphone5s
        代码如下:#import "ViewController.h"

        @interface ViewController ()
        @property (nonatomic, strong) JSContext *jsContext;

        @EnD

        @Implementation ViewController

        - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.

        NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
        [self.webViewIn loadRequest:[[NSURLRequest alloc] initWithURL:url]];
        }

        - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
        }

        - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        self.jsContext[@"Toyun"] = self;
        self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息:%@", exceptionValue);
        };
        }

        #pragma mark - JSObjcDelegate

        - (void)callCamera {
        NSLog(@"callCamera");
        // 获取到照片之后在回调js的方法picCallback把图片传出去
        JSValue *picCallback = self.jsContext[@"picCallback"];
        [picCallback callWithArguments:@[@"photos"]];
        }

        - (void)share:(NSString *)shareString {
        NSLog(@"share:%@", shareString);
        // 分享成功回调js的方法shareCallback
        JSValue *shareCallback = self.jsContext[@"shareCallback"];
        [shareCallback callWithArguments:nil];
        }
        TIME_for:@David_YangSong 怎么解决的,什么原因引起的?
        小酒窝_David:@Toyun 可以了,谢谢
        TIME_for:@David_YangSong ViewController遵守JSObjcDelegate这个协议了没有?
      • 75040dae73e8:6666666
        TIME_for:@C己 ??有疑问和错误可以指出来。。
      • 手机用户2484935965:不错,多多分享,怎么联系交流交流
        TIME_for:@手机用户2484935965 好的,非常欢迎,有错误也欢迎指正~~
        手机用户2484935965: @Toyun 秒回了啊,6,我码了代码再议
        TIME_for:@手机用户2484935965 谢谢肯定,有问题可简信密我~~
      • 6712ed0d908a:很棒的文章,不过我有一个问题,第二个例子不是拦截Toyun嘛,但是我测试的时候发现.m的url里的协议头变成了纯小写的toyun,这个是为什么呢?
        TIME_for:@loginSin JSPatch略有研究,它也是用的JavaScriptCore.framework作为JS引擎,大家好像都是比较喜欢用它来控制远程bug啊。但是一定要注意安全,注意安全,注意安全,重要事情说三遍。结合RN会有更广阔的天地。可以一起研究啊!
        6712ed0d908a:@Toyun 不知道博主对JSPatch有没有研究,最近想研究研究
        TIME_for:@6712ed0d908a 这个是因为请求的URL把协议头给自动处理了,非常感谢你的指正,我自己还没注意,这处错误,文章已经更新。给你带来误解和不便之处,敬请谅解~~
      • 5a72722962dd:好
        TIME_for:@kile 谢谢~~
      • 39f2579981fb:不错
        TIME_for:@39f2579981fb 谢谢肯定
      • Jon1993:mark
        TIME_for:@Jon1993 共同进步~~
      • 懒惰的习惯:不错,值得mark
        TIME_for:@婷空万里 谢谢~~,共同进步
      • HenryCheng:写的不错,这个方法还没有用过,之前复杂的都是用WebViewJavaScriptBridge,简单的就直接用- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest拦截请求,但是如果是Apache的话,好像这个方法捕捉不到,因为界面就不会刷新,楼主有好的方法没
        TIME_for:@HenryCheng 谢谢肯定,共同交流,共同进步。
        HenryCheng:@Toyun 额,之前做的一个项目,URL找不到了,就是之前遇到的一个问题,那时候还没找到解决办法,最后换了一个思路解决的,无论如何你这文章写的不错,赞一个!
        TIME_for:@HenryCheng 这个理论上讲是没问题的,能否把部署到Apache的这个页面的URL给我一下,或许可以试着分析一下,找下具体原因。楼主这方面也不是很擅长,非常抱歉~~
      • 塔罗师_Michael:nice
        TIME_for:@ysghome 谢谢
      • 盛夏光年yes:刚好在看js桥的交互知识。分享的很细腻哦!
        TIME_for:@盛夏光年yes 多多交流,共同进步~~
      • J_Knight_:请问笔者开篇的思维导图是用什么软件画的?
        J_Knight_:@jvsugar的v 好的 谢谢
        Sugar_璟:@Hero_SJ mindnode
      • 6605eae1686f:我正遇到怎么将请求到的网页数据,与一个html模板以及css和javascript相互关联呢😊!关于这方面的内容,楼主有研究吗
        TIME_for:@征天紫龙 可以看出,所在公司应该是个大公司,web页面这样处理,可能就类似网易新闻详情页那样。这里给你推荐一篇文章吧,他下面附带了demo。写的还是很不错的。http://386502324.blog.163.com/blog/static/11346937720154293438399/ 评论里面这个问题也不好说清楚啊。非常抱歉~~
      • 买了否冷_:nice 帮我敲开了交互的大门 :smile:
        买了否冷_:@Toyun 好的!我们前端那边希望用js下发指令让我做他的服务端返回通讯录,通话记录之类的😱
        TIME_for:@拉莫斯 可以研究一下除本文介绍的其他方法啊~~
      • 076f37cc3cd0:很棒的一篇文章
        TIME_for:@gojan 谢谢夸奖,共同进步~~

      本文标题:Objective-C与JavaScript交互的那些事

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