iOS中使用JavaScriptCore实现Objective-C和JavaScript的相互调用

最近看了一个对Github上面编程语言使用统计的排行榜,JavaScript真可以说是一枝独秀,很难想象20年前,这个语言只是浏览器中的装饰性语言,能做的事情也就是一点特效或者检查一下要提交给服务器的表单是否满足要求。今天的JavaScript已经是一个全栈语言,从客户端到服务器无所不在。很多编程语言都提供了跟JavaScript进行交互的接口,这一点在iOS开发中也不例外。

??iOS7以前,在App中调用JavaScript的方式只有一种,就是通过UIWebView对象的stringByEvaluatingJavaScriptFromString:方法。由于UIWebView中包含了CSS渲染引擎和JavaScript执行引擎(说白了就是微型一个浏览器),因此这个方法可以让UIWebView通过它的JavaScript运行时环境执行JavaScript代码,但是能做的事情非常有限,我们可以先看看下面的例子。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    // 获得UIWebView中加载页面的标题
    NSString *title = [webView stringByEvaluatingJavaScriptFromString:
        @"document.title"];
    NSLog(@"%@", title);
    // 获得UIWebView中加载页面的链接地址
    NSString *urlStr = [webView stringByEvaluatingJavaScriptFromString:
        @"location.href"];
    NSLog(@"%@", urlStr);
}

??从iOS7开始,我们可以使用JavaScriptCore框架来让我们的Objective-C代码和JavaScript进行深度交互,简单的说我们可以在Objective-C代码中访问JavaScript中的变量或调用JavaScript的函数,也可以JavaScript中使用Objective-C的对象和方法。我们可以先看一个简单的例子。

先加入JavaScriptCore的头文件。

#import <JavaScriptCore/JavaScriptCore.h>

在Objective-C中使用JavaScript的正则表达式验证字符串。

    // 创建JavaScript执行环境(上下文)
    JSContext *context = [[JSContext alloc] init];
    NSString *funCode =
        @"var isValidNumber = function(phone) {"
         "    var phonePattern = /^1[34578]\\d{9}$/;"
         "    return phone.match(phonePattern);"
         "};";
    // 执行上面的JavaScript代码
    [context evaluateScript:funCode];
    // 获得isValidNumber函数并传参调用
    JSValue *jsFunction = context[@"isValidNumber"];
    JSValue *value1 = [jsFunction callWithArguments:@[ @"13012345678" ]];
    NSLog(@"%@", [value1 toBool]? @"有效": @"无效");    // 有效
    JSValue *value2 = [jsFunction callWithArguments:@[ @"12345678899" ]];
    NSLog(@"%@", [value2 toBool]? @"有效": @"无效");    // 无效

在Objective-C中调用JavaScript函数求阶乘。

    // 创建JavaScript执行环境(上下文)
    JSContext *context = [[JSContext alloc] init];
    // 可以将一个block传给JavaScript上下文
    // 它会被转换成一个JavaScript中的函数
    context[@"factorial"] = ^(int x) {
        double result = 1.0;
        for (; x > 1; x--) {
            result *= x;
        }
        return result;
    };
    // 执行求阶乘的函数
    [context evaluateScript:@"var num = factorial(5);"];
    JSValue *num = context[@"num"];
    NSLog(@"5! = %@", num);    // 5! = 120

??JavaScript和Objective-C中类型的对应关系如下表所示:

Objective-C类型 JavaScript类型
nil undefined
NSNull null
NSString string
NSNumber number, boolean
NSDictionary Object object
NSArray Array object
NSDate Date object
NSBlock Function object
id Wrapper object
Class Constructor object

??

??再来看一个例子。我们在根视图控制器中放置一个按钮,点击后会导航到下一个视图控制器,其中有一个UIWebView加载了一个网页,页面中有一颗按钮,我们希望点击按钮后导航到上一个视图控制器,要做到这一点,就需要在JavaScript中访问Objective-C对象和方法。

页面的代码:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>测试页面</title>
        <style type="text/css">
            #backButton {
                display: inline-block;
                width:50px; height:30px;
            }
        </style>
    </head>

    <body>
        <button id="backButton">返回</button>
        <script type="text/javascript">
            var btn = document.getElementById("backButton");
            var cb = function() {
                window.alert(‘Hello‘);
            };
            btn.addEventListener(‘click‘, cb, false);
        </script>
    </body>
</html>

第二个视图控制器的代码:

#import "ViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>

@protocol MyProtocol <JSExport>

- (void) letsGoBack;

@end

@interface SecondViewController : ViewController <MyProtocol>

@end
@interface SecondViewController () <UIWebViewDelegate>

@property (weak, nonatomic) IBOutlet UIWebView *myWebViw;

@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [_myWebViw loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:
        @"http://localhost:8080/myweb/test.html"]]];
    _myWebViw.delegate = self;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    // 通过UIWebView获得网页中的JavaScript执行环境
    JSContext *context = [webView valueForKeyPath:
        @"documentView.webView.mainFrame.javaScriptContext"];
    // 设置处理异常的block回调
    [context setExceptionHandler:^(JSContext *ctx, JSValue *value) {
        NSLog(@"error: %@", value);
    }];

    context[@"callBackObj"] = self;
    // 下面的代码移除了按钮原先绑定的事件回调重新绑定返回上一个视图控制器的代码
    NSString *code =
                   @"var btn = document.getElementById(‘backButton‘);"
                    "btn.removeEventListener(‘click‘, cb);"
                    "btn.addEventListener(‘click‘, function() {"
                    "   callBackObj.letsGoBack();"
                    "});";
    [context evaluateScript:code];
}

// 实现协议中的方法
- (void) letsGoBack {
    // 必须回到主线程刷新用户界面
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.navigationController popViewControllerAnimated:YES];
    });
}

@end

??可以在Github上下载到上面例子的完整代码。

时间: 2024-09-30 07:01:50

iOS中使用JavaScriptCore实现Objective-C和JavaScript的相互调用的相关文章

Android中通过WebView控件实现与JavaScript方法相互调用的地图应用

在Android中通过WebView控件,可以实现要加载的页面与Android方法相互调用,我们要实现WebView中的addJavascriptInterface方法,这样html才能调用android方法,在这里我个人觉得有点和DWR相似. 为了让大家容易理解,我写了一个简单的Demo,具体步骤如下: 第一步:新建一个Android工程,命名为WebViewDemo(这里我在assets里定义了一个html页面). 第二步:修改main.xml布局文件,增加了一个WebView控件还有But

苹果系统开发中的混合编程(2):Swift和C的相互调用

在进行Swift和C之间的相互调用时,有必要先了解一下两种语言之间的类型转换关系: C 类型 Swift 类型 bool CBool char, signed char CChar unsigned char CUnsignedChar short CShort unsigned short CUnsignedShort int CInt unsigned int CUnsignedInt long CLong unsigned long CUnsignedLong long long CLon

js中的js原生对象和jquery对象的相互调用

js中js原生对象是不能使用jquery对象的方法的,比如 jsobj.html()这样是调用不了的,同时$('jqobj').innerHTML 同样是错误的的 实例代码: $(function() { // console.log($('.contentdiscuss')[0].innerHTML.length) var lctobj={}; lctobj.lookmore=(function(){ var ct=$('.contentdiscuss'); for(var i=0;i<ct.

Hybrid App开发模式中, IOS/Android 和 JavaScript相互调用方式

IOS:Objective-C 和 JavaScript 的相互调用 iOS7以前,iOS SDK 并没有原生提供 js 调用 native 代码的 API.但是 UIWebView 的一个 delegate 方法使我们可以做到让 js 需要调用时,通知 native.在 native 执行完相应调用后,可以用stringByEvaluatingJavaScriptFromString 方法,将执行结果返回给 js.这样,就实现了 js 与 native 代码的相互调用.具体让 js 通知 na

IOS中实现单例

在IOS中,所有对象的内存分配的方法都会调用allocWithZone,比如构造函数alloc,所以重写这个方法就可以实现单例. Xcode中预先写好了实现代码的快捷指令,敲dispatch_once就会看到.这个是有GCD实现的单例代码. 实现代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +(id)allocWithZone:(struct _NSZone *)zone {     static LYDemo * instance;     //

iOS原生和H5的相互调用

为什么现在越来越多的APP中开始出现H5页面? 1,H5页面开发效率更高,更改更加方便: 2,适当缩小APP安装包的大小: 3,蹭热点更加方便,比如五一,十一,双十一搞活动: 那么为什么说H5无法取代原生的APP,只能处在一个共存的例子呢? 1,这个是由系统的底层决定的,极端例子,所有的应用都通过H5展示,那么你是否需要一个浏览器? 2,涉及庞大的功能,涉及复杂的逻辑结构,涉及安全性的要求,H5可以胜任吗? 所以,H5和原生的融合会出现动态的调和,最终会找到一个平衡. 那么接下来就说下iOS开发

iOS中的MVC

我们今天谈谈cocoa程序设计中的 模型-视图-控制器(MVC)范型.我们将从两大方面来讨论MVC: 什么是MVC? M.V.C之间的交流方式是什么样子的? 理解了MVC的概念,对cocoa程序开发是至关重要的. 一.MVC的概念 MVC是Model-VIew-Controller,就是模型-视图-控制器,这些都是什么东西呢? MVC把软件系统分为三个部分:Model,View,Controller.在cocoa中,你的程序中的每一个object(对象)都将明显地仅属于这三部分中的一个,而完全不

iOS 中的 HotFix 方案总结详解

相信HotFix大家应该都很熟悉了,今天主要对于最近调研的一些方案做一些总结.iOS中的HotFix方案大致可以分为四种: WaxPatch(Alibaba) Dynamic Framework(Apple) React Native(Facebook) JSPatch(Tencent) WaxPatch WaxPatch是一个通过Lua语言编写的iOS框架,不仅允许用户使用 Lua 调用 iOS SDK和应用程序内部的 API, 而且使用了 OC runtime 特性调用替换应用程序内部由 O

IOS 中的JS

 文章摘自: http://www.cocoachina.com/ios/20150127/11037.html  JSContext/JSValue JSContext 即JavaScript代码的运行环境.一个Context就是一个JavaScript代码执行的环境,也叫作用域.当在浏览器中运行 JavaScript代码时,JSContext就相当于一个窗口,能轻松执行创建变量.运算乃至定义函数等的JavaScript //Objective-C JSContext *context = [