oc 與js 的原生互動

weixin_33860722發表於2017-05-04

參考


總評:

oc 與js的互動,1.有原生的方式,oc 調js簡單,js調oc 麻煩(協議攔截"實現的互動方式)。

2.iOS7之前,蘋果沒有出 JavaScriptCore 之前,業界普遍採用開源庫WebViewJavascriptBridge和EasyJSWebView來解決的,原理都是基於攔截協議的封裝,

3.iOS7之後,蘋果針對JavaScript出了一個官方的庫JavaScriptCore,是Objective-C封裝了WebKit的JavaScript引擎,使我們可以脫離WebView執行JS程式碼。所以在iOS7之後想要實現互動,採用JavaScriptCore也是一種不錯的選擇,前提是你的專案不需要相容到iOS7之前(覺得現在應該不那麼強調要相容到iOS7了吧)

4.iOS8釋出的時候,蘋果又推出了WKWebView,對之前的UIWebView進行了一次脫胎換骨的重構(將UIWebView和UIWebViewDelegate重構成了14個類和3個協議),功能也更加完善和強大,穩定性和效能也明顯提高。


一,方法互調(首先宣告的是無論如何互調,都需要兩邊配合的,有點像與後臺的互動)

1.1 oc 呼叫js方法

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

示例:

if (sender.tag == 234) {
    [self.webView stringByEvaluatingJavaScriptFromString:@"alertSendMsg('18870707070','週末爬山真是件愉快的事情')"];
}
//引數無限制

直接oc呼叫js的函式程式碼,也是強悍!

對應的js:

<html>
    <!--描述網頁資訊-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>小黃</title>
        <style>
            .btn{height:40px; width:60%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1.2em; color: white}
        </style>
        
        <script>
            
            //提供給OC呼叫JS的方法列表

    function alertSendMsg(num,msg) {
                alert('這是我的手機號:' + num + ',' + msg + '!!')
            }
       
        </script>
 </head>

</html>

1.2 js呼叫oc 的方法(那就麻煩咯)

js那邊不麻煩,也就加個連結

<html>
    <!--描述網頁資訊-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>小黃</title>
        <style>
            .btn{height:40px; width:60%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1.2em; color: white}
        </style>
        
        <script>
  
            //JS響應方法列表        
            function btnClick3() {
                location.href = "rrcc://showSendNumber_msg_?13300001111&go climbing this weekend"
            }

        </script>
 
    </head>

    <!--網頁具體內容-->
    <body>
        <br/><br/>

        <div>
            <label>小黃:13300001111</label>
        </div>
        <br/><br/>

        <div>
            <button class="btn" type="button" onclick="btnClick3()">發簡訊給小紅</button>
        </div>

    </body>
</html>

ps:"_"用作OC方法名中冒號的替換(這個應該是特殊字元的轉義吧)

麻煩的是oc這邊(其實就是對js傳過來的url進行處理,分解出方法名和引數,通過OC執行選擇器(selector)方法,來實現,不過這種方式最多隻能傳遞引數的個數為2個,如果需要多個引數,可以從資料結構的組織方面入手)

#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    
    //OC呼叫JS是基於協議攔截實現的 下面是相關操作
    NSString *absolutePath = request.URL.absoluteString;
    
    NSString *scheme = @"rrcc://";
    
    if ([absolutePath hasPrefix:scheme]) {
        NSString *subPath = [absolutePath substringFromIndex:scheme.length];
        
        if ([subPath containsString:@"?"]) {//1個或多個引數
            
            if ([subPath containsString:@"&"]) {//多個引數
                NSArray *components = [subPath componentsSeparatedByString:@"?"];
                
                NSString *methodName = [components firstObject];
                
                methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
//這段處理後的methodName結果是@"showSendNumber:msg:"(為了利用OC執行選擇器(selector)方法,來實現)
                SEL sel = NSSelectorFromString(methodName);

                NSString *parameter = [components lastObject];
                NSArray *params = [parameter componentsSeparatedByString:@"&"];
                
                if (params.count == 2) {
                    if ([self respondsToSelector:sel]) {
//OC執行選擇器(selector)方法關鍵是這一句了
                        [self performSelector:sel withObject:[params firstObject] withObject:[params lastObject]];
                    }
                }
                

            } else {//1個引數
                NSArray *components = [subPath componentsSeparatedByString:@"?"];
                
                NSString *methodName = [components firstObject];
                methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
                SEL sel = NSSelectorFromString(methodName);

                NSString *parameter = [components lastObject];
                
                if ([self respondsToSelector:sel]) {
                    [self performSelector:sel withObject:parameter];
                }

            }
                
        } else {//沒有引數
            NSString *methodName = [subPath stringByReplacingOccurrencesOfString:@"_" withString:@":"];
            SEL sel = NSSelectorFromString(methodName);
            
            if ([self respondsToSelector:sel]) {
                [self performSelector:sel];
            }
        }
    }
    
    return YES;
}

二,內容上的互動

就目前所知,oc 直接修改webview的內容比較方便,但是,js 想直接修改oc 的介面內容貌似不可以。(不知道是不是蘋果方面出於安全方面的考慮,不給隨隨便便的一個網頁這種許可權?)

2.1 oc 修改webView的內容

這些操作,貌似都是在這個delegate裡進行的

// 網頁檢視載入完畢會呼叫代理的這個方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{

}

2.1.1 取

首先必須強調的是除非有且僅有唯一的標籤,比如網頁只有一個<title></title>

那麼就可以直接拿

NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
//獲取URL  
    NSString *curURL = [webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];  

而有多個<div></div>確沒有標示,是拿不了的(不過這種情況少啊)

2.1.2 改

  //修改屬性值  
    NSString *js_result = [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByName('good')[0].color='red';"];

2.1.3 刪

  NSString *str = @"document.getElementsByClassName('detail_btns2')[0].remove();";
    [webView stringByEvaluatingJavaScriptFromString:str];

2.1.4
好像找不到“增”和“查”啊,擦~


最後
當然也有一些封裝的庫,不過還是建議用蘋果提供的吧(JavaScriptCore)

相關文章