天天看点

JS与OC 互相调用

1、JS调用OC

如果是结合UIWebview来使用的话,至少有两种方式:

1)地址重定向,定义URL协议。就是将一些要调用的方法和参数拼成URL。然后再去截取解析。使用PerformSelector:方法进行回调。

在加载网页发送请求时会调用

webView:shouldStartLoadWithRequest:navigationType:

代理方法。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSURL *url = [request URL];
    NSString *urlStr = request.URL.absoluteString;
    NSString *scheme = @"test://";
    if ([urlStr hasPrefix:scheme]) {
        // 1.截取方法名称
        // methodName1_methodName2_?param1&param2
        NSString *temp = [urlStr substringFromIndex:scheme.length];
        NSArray *subPaths = [temp componentsSeparatedByString:@"?"];
        NSString *methodName = [subPaths firstObject];
        methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        // 2.将截取出来的字符串转换为SEL
        SEL selector = NSSelectorFromString(methodName);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        // 3.切割所有的参数
        if (subPaths.count == ) {
            NSArray *parms = [[subPaths lastObject] componentsSeparatedByString:@"&"];
            if (parms.count == ) {
                [self performSelector:selector withObject:[parms firstObject] withObject:[parms lastObject]];
            }else
            {
                [self performSelector:selector withObject:[subPaths lastObject]];
            }
            return NO;
        }
#pragma clang diagnostic pop
        // 3.利用performSelector调用OC方法
        [self performSelector:selector withObject:nil];
        return NO;
    }
    NSLog(@"正在请求");
    return YES;
}
           

此段代码由于使用的performSelector系列的方法,所以对参数的个数是有限制的。如果需要传递多个参数,那么可以考虑使用NSInvocation这个类。不过在iOS7以后,我们一般就不用这种方式了,而是使用JavaScriptCore.framework框架提供的API来处理。

2)使用JavaScriptCore.framework库

使用JavaScriptCore.framework库进行JS调用OC时,使用jsContext[“js方法名”]就可以完成。因为这个jsContext[“js方法名”]后接一个block,该block中包含要执行的OC代码(block相当于一个匿名的函数,当从JS代码中触发该方法的时候,就相当于js调用了OC的方法)。

下面给出一个应用例子。我们加载一个html网页,当点击该网页中的一个按钮时,触发一段JS函数的调用,该JS函数调用在OC中注册的方法(jsContext[“js方法名”])。该网页的代码如下:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>百度一下,你就知道</title>
        <style type="text/css">

        body{
            width: ;
            margin: ;
            padding-bottom: ;
            padding-left: px;
            padding-right: px;
            padding-top: ;
        }
        #baiduimage{
            margin-left: %;
            margin-top: %;
        }
        form{
            margin-left: %;
            margin-top:px;
        }
        #text{
            width: px;
            height: px;
            border-color:#39F;
            border-style:solid;
            border-width:px;
            font-size:px;
        }
        button{
            width:px;
            height:px;
            background-color:#39F;
            font-size: px;
        }
        div footer{
            margin-left: %;
            margin-bottom: %;
            margin-top: px;
            margin-bottom: auto;
            font-size: px;
        }
        </style>
        <script>
        function repost()
        {
            location.href = "test://sendMessageWithNumber_andContent_?10086&love";
        }
        function sum()
        {
            return  + ;
        }
        function JSLoadOCMethod()
        {
            var str = logForTest("hello jsLoadOCMethod!");
            alert(str);
        }
        </script>
</head>

    <body>
        <div class="all">
            <div>
                <img src="baidu.png"  alt="" width="270" height="129" id="baiduimage"/>
            </div>
            <div>
                <form>
                    <input name="input" type="text" id="text" value="百度一下,你就知道" maxlength="100">
                    <button  onclick="JSLoadOCMethod();">百度一下</button>
                </form>
            </div>
            <div>
                <footer>
                    <span>&copy;2016&nbsp;baidu&nbsp;<a href="http://www.baidu.com/duty/">使用百度前必读</a>&nbsp;京ICP证030173号<img width="13" height="16" data-loadfunc="1" src="copy_rignt_24.png" data-loaded="1"></span>
                </footer>
            </div>
        </div>
    </body>
</html>
           

加载出来的网页效果如下:

JS与OC 互相调用

当我们点击蓝色的“百度一下”这个按钮时,会触发绑定的函数,然后在该绑定的函数中我们调用了一个

logForTest

的函数,而该函数并没有在js代码中实现,但是我们在OC代码中会通过

jsContext["logForTest"]

进行注册。之后就是触发了

jsContext["logForTest"]

所赋值的block代码块。OC的实现代码如下:

- (void)JSLoadOCMethod {
    self.context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.context[@"logForTest"] = ^(){
        NSLog(@"Begin Log");
        NSArray *args = [JSContext currentArguments];

        for (JSValue *jsVal in args) {
            NSLog(@"%@", jsVal);
        }

        JSValue *this = [JSContext currentThis];
        NSLog(@"-------End Log----%@---",this);
        return @"Are you OK?";
    };
}
           

测试的结果如下:

JS与OC 互相调用

在这里同时将OC中block的返回值返回到了所调用的js函数里,并且显示出来。这样也就做到了JS和OC值之间的互相传递。

这里可能有一个注意点,就是

JSLoadOCMethod

方法所放的位置,如果你放在控制器的

viewDidLoad

方法中,那么不管你点多少次按钮,上面的block只会执行一次。这是因为当我们点击按钮时页面重新加载,所注册的JSContext已经失效。所以如果我们把JSContext的注册放在

viewDidLoad

方法中就无法实现JSContext的重新注册。每次重新注册都会执行,

webViewDidFinishLoad:

方法,所以我们可以将

JSLoadOCMethod

方法放在该方法中。对于我们这种情况,由用户发起触发了一个JS调用OC的方法是没有问题的,但如果你的网页在一开始加载的时候,就要触发这个JS调用OC的方法可能是不合适的,因为

webViewDidFinishLoad:

方法还没有执行。其中一个解决方法是:在该方法中触发OC调用JS函数,然后,再由JS函数调用JSContext中注册的方法。

2、OC调用JS

如果是使用UIWebView,那么可以使用它所提供的一个方法

stringByEvaluatingJavaScriptFromString:

该方法的参数是一个字符串类型的JS代码。

当然我们也可以使用JavaScriptCore.framework库提供的OC调用JS的API方法。如:

evaluateScript:

evaluateScript: withSourceURL:

方法。

示例代码:

继续阅读