天天看點

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:

方法。

示例代碼:

繼續閱讀