【iOS & Web】JavaScript & Objective-C二重奏

1、JS call OC

方法1:

通攔截協議頭來獲取協議字符串。在UIWebView中的代理方法中有這樣的方法,以下圖所示:javascript

//UIWebView每次請求內容以前,都會調用這個方法,經過返回YES/NO來決定UIWebView是否進行request請求。
//咱們能夠經過URL的協議頭及字符串來區別普通的URL請求
//JS傳遞給OC的參數能夠經過URL帶過來,若是參數內容過長能夠經過post請求來傳遞,本地在攔截request後,能夠將HTTPBody中的請求內容解析出來。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    return YES;
}

下面是我寫的簡單的H5頁面經過JS請求一個自定義協議的URL,而後經過UIWebView來攔截:html

Demo1:

第一種: 無參數的協議URL

H5和JS代碼以下:java

<!-- H5代碼 -->
	<input type="button" onclick="shareWexin()" value="無參協議的URL">

	<!-- JS 代碼 -->
	<script type="text/javascript">
		function shareWexin() {
			//這裏objc是協議頭,做爲iOS端攔截的標識
			//shareWX即爲‘協議名’、做爲iOS方法的識別
			window.location.href="objc://shareWX";
		}
	</script>

UIWebView代理中的協議攔截:web

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    NSString *urlString = [[request URL] absoluteString];
    
    NSLog(@"協議URL => %@",urlString);
    
    NSArray *urlComps = [urlString componentsSeparatedByString:@"://"];
    
    if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){
        
        NSLog(@"方法名 => %@",urlComps.lastObject);
        
        return NO;
    }
    return YES;
}

點擊網頁中的按鈕打印以下所示:異步

如今咱們成功攔截到了協議,後續的處理就不用我多說了吧!post

第二種: 有參數的協議URL

H5和JS代碼以下:測試

<body>
	<h2>第一種方法</h2>
	<h4>請求有參協議的URL</h4>
	<!-- H5代碼 -->
	<input type="button" onclick="shareQQ()" value="有參協議的URL">

	<!-- JS 代碼 -->
	<script type="text/javascript">
		function shareQQ() {
			//這裏objc是協議頭,做爲iOS端攔截的標識
			//shareWX即爲‘協議名’、做爲iOS方法的識別
			//type=qq 即爲參數
			window.location.href="objc://shareQQ?type=qq";
		}
	</script>
</body>

UIWebView代理中的協議攔截:lua

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    NSString *urlString = [[request URL] absoluteString];
    
    NSLog(@"協議URL => %@",urlString);
    
    NSArray *urlComps = [urlString componentsSeparatedByString:@"://"];
    
    if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){
        
        NSLog(@"方法名及參數URL => %@",urlComps.lastObject);
        
        NSString * parmeterURL = [urlComps lastObject];

        //獲取參數
        NSArray * parmeters = [parmeterURL componentsSeparatedByString:@"?"];
        
        NSString * parStr = [parmeters lastObject];
        
        NSLog(@"攜帶參數的字符串 => %@",parStr);
        
        return NO;
    }
    return YES;
}

點擊網頁中的按鈕打印以下所示:url

如今咱們成功攔截到了協議中的方法名和參數,這樣你就能夠進行後續的處理了!spa

問題:

這種方法主要是經過JS調用一個咱們本身的通用協議URL,iOS端經過攔截的方法,來達到JS調用OC的目的,可是這存在很多問題,好比JS若是連續請求兩次以上協議,iOS端只能攔截最後一個,這樣就沒法真正的作到JS調用OC;另外經過攔截的方法的在執行效率上比較慢!

那麼還有更好的方法麼?固然這就須要咱們用到JavaScriptCore來實現了,一下是第二種方法:

 

方法2:

經過注入JS代碼來消除JS異常狀況,並實現上下文的Block回調,來實現調用OC的目的,下面爲具體的步驟:

1、引入JavaScriptCore

@import JavaScriptCore;

2、在頁面加載完成後,獲取JS上下文:

//獲取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

3、注入JS代碼來觸發block:

//定義好JS要調用的方法,如shareWB
    context[@"shareWB"] = ^() {
        
        // your code
    };

Demo2:

第一種: 無參數的方法

經過JS調用和iOS端協商好的方法,在JS中並未實現,因此在JS中屬於異常的狀況,下面爲H5和JS代碼:

<h2>第二種方法</h2>
<input type="button" onclick="shareWebo()" value="第二種方法">

<script type="text/javascript">
    function shareWebo() {
        //這是和iOS端協商好的方法,在JS中並未實現,因此在JS中屬於異常的狀況
	    shareWB();
    }
</script>

捕獲異常,即注入JS代碼使消除異常狀況,OC主要代碼:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"頁面加載完成");
    
    //獲取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //定義好JS要調用的方法,如shareWB
    context[@"shareWB"] = ^() {
        
        NSLog(@"shareWB 我被調用了!");
        
        NSArray *args = [JSContext currentArguments];

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

點擊網頁中的按鈕打印以下所示:

第二種: 有參數的方法

下面爲H5和JS代碼:

<h4>有參數的方法</h4>
<input type="button" onclick="shareMessage()" value="有參數的方法">

<script type="text/javascript">
	function shareMessage(){
		//帶參數的 未實現的方法, JS中一樣屬於異常的狀況
		shareMsg("p1","p2");
	}
</script>

OC主要代碼:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"頁面加載完成");
    
    //獲取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    context[@"shareMsg"] = ^() {
        
        NSLog(@"shareMsg 我被調用了!");
        
        NSArray *args = [JSContext currentArguments];
        
        for (JSValue *jsVal in args) {
            NSLog(@"參數 => %@", jsVal.toString);
        }
        
    };
    
}

點擊網頁中的按鈕打印以下所示:

問題:

能夠同時觸發多個事件麼?如下是測試結果:

在JS中調用兩個方法:

<script type="text/javascript">

		function shareMessage(){
			//帶參數的 未實現的方法, JS中一樣屬於異常的狀況
			shareWB();
			shareMsg("p1","p2");
		}
	</script>

OC中的打印結果:

經測試,答案是確定的!

 

下面咱們探討一下OC Call JS:

2、OC Call JS

方法一:

經過向UIWebView注入JS方法來調用JS內部的方法,以下所示:

- (IBAction)callJS:(id)sender {
    //調用js方法的字符串
    NSString *jsStr = [NSString stringWithFormat:@"alertMsg('%@')",@"提示的信息"];
    
    //將js注入webView
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
}

如下是JS主要代碼:

<script type="text/javascript">
		//被oc調用的方法
		function alertMsg(msg) {
			alert(msg);
		}
	</script>

如下是運行的效果,看這個彈出能夠JS調用的哦:

方法二:

使用JavaScriptCore來和JS交互

- (IBAction)callJS:(id)sender {
    //獲取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    //JS代碼字符串
    NSString *JS = @"alertMsg('提示信息2')";
    [context evaluateScript:JS];
}

如下是JS主要代碼:

<script type="text/javascript">
		//被oc調用的方法
		function alertMsg(msg) {
			alert(msg);
		}
	</script>

如下是運行的效果,看這個彈出能夠JS調用的哦:

注意:

以上兩種方法都是同步方法,若是JS比較耗時,會形成界面卡頓,建議將JS耗時的程序放在異步線程中執行!好比alert()方法就會阻塞UI主線程(注意:按鈕‘注入JS’此時爲高亮狀態,說明彈窗阻塞了主線程,在等待用戶響應),咱們能夠經過setTimeout()方法來實現異步執行的目的,以下所示:

<script type="text/javascript">
        //被oc調用的方法
		function alertMsg(msg) {
            //異步執行
			setTimeout(function(){
         		alert(msg);
         	},1);
		}
	</script>

如下爲運行結果,‘注入JS’按鈕恢復了正常狀態,說明此時alert()是異步執行的。


相關文章
相關標籤/搜索