iOS與JS交互的方法:
1.攔截url (適用于UIWebView和WKWebView)
2.JavaScriptCore (只適用于UIWebView, iOS7+)
3.WKScriptMessageHandler(只適用與WKWebView, iOS8+)
4.WebViewJavascriptBridge (適用于UIWebView和WKWebView, 屬于第三方框架.以后會單講這個框架)
方法一.攔截url
(1) web調(diào)用原生:
<1> 和后端同事協(xié)定好協(xié)議腮出,如jxaction://scan 表示啟動二維碼掃描.
jxaction://location表示獲取定位恒削。
<2> 實現(xiàn)UIWebView代理的shouldStartLoadWithRequest: navigationType:方法, 在方法中對url進(jìn)行攔截,如果是步驟<1>中定義好的協(xié)議則執(zhí)行對應(yīng)原生代碼,返回false, 否則返回true繼續(xù)加載原url娶吞。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if ([request.URL.absoluteString isEqualToString:@"jxaction://scan"]) {
//調(diào)用原生掃描二維碼
return NO;
}
return YES;
}
h5代碼:
<a href="jxaction://scan">掃一掃(攔截url)</a>
(2)原生調(diào)用js
若(1)中掃描二維碼結(jié)束后, 需要把掃描結(jié)果返回給web頁,直接調(diào)用UIWebView的stringByEvaluatingJavaScriptFromString方法,或者WKWebView的evaluateJavaScript: completionHandler:方法
//回調(diào)方法
[self.webView stringByEvaluatingJavaScriptFromString:@"scanResult('我是掃描結(jié)果~')"];
[self.wkWebView evaluateJavaScript:@"scanResult" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//回調(diào)結(jié)果
}];
方法二. JavaScriptCore
方法一web調(diào)用原生只適合簡單的調(diào)用称簿,如果要傳遞參數(shù), 雖然也可以拼接在url上, 如jxaction://scan?method=aaa, 但是需要我們自行對字符串進(jìn)行分割解析,并且特殊字符需要編碼。在iOS7系統(tǒng)提供了JavaSciptCore议蟆,可以更優(yōu)雅地實現(xiàn)js與原生交互.
(1) js調(diào)用原生
<1>新建類繼承自NSObject(如AppJSObject)
<2>.h文件中聲明一個代理并遵循JSExport,代理內(nèi)的方法和js定義的方法名一致.
<3>.m文件中實現(xiàn)<2>代理中對應(yīng)的方法萎战,可以在方法內(nèi)處理事件或通知代理.
AppJSObject.h
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
NS_ASSUME_NONNULL_BEGIN
@protocol AppJSObjectDelegate <JSExport>
- (void)scan:(NSString *)message;
@end
@interface AppJSObject : NSObject
@property (nonatomic, weak) id<AppJSObjectDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
AppJSObject.m
#import "AppJSObject.h"
@implementation AppJSObject
- (void)scan:(NSString *)message{
[self.delegate scan:message];
}
@end
h5代碼:
<input type="button" name="" value="掃一掃" onclick="scan()">
<br/>
<p id="result">掃描結(jié)果:</p>
<script type="text/javascript">
//調(diào)用APP的掃描方法 h5->app
function scan(){
app.scan('scanResult');
}
//掃描結(jié)果回調(diào)方法 app->h5
function scanResult(result){
document.getElementById("result").innerHTML = '掃描結(jié)果:' + result;
}
</script>
<4>在UIWebView加載完成的代理中把AppJSObject實例對象類注入到JS中,那么在js中調(diào)用方法就會調(diào)用到原生AppJSObject實例對象中對應(yīng)的方法了咐容。
//將AppJSObject實例注入到JS中 那么在js中調(diào)用方法就會調(diào)用到原生AppJSObject實例對象中對應(yīng)的方法了。
- (void)webViewDidFinishLoad:(UIWebView *)webView{
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
AppJSObject *jsObject = [[AppJSObject alloc] init]; //AppJSObject的實例
jsObject.delegate = self;
context[@"app"] = jsObject;
}
代碼:https://github.com/dolacmeng/JSDemo/tree/master
*也可以通過block實現(xiàn)而不創(chuàng)建新類AppJSObject:
context[@"openAlbum"] = ^(){
NSLog(@"js調(diào)用oc打開相冊");
};
(2)原生調(diào)用js
可以通過一中的方法, 也可以通過JSContext:
JSContext *context=[_mainWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
NSString *alertJS= [NSString stringWithFormat:@"%@('%@')",_photoMethod,fileUrl];
[context evaluateScript:alertJS];
方法三. WKScriptMessageHandler
現(xiàn)在很多app都是支持iOS8+蚂维,很多人使用WKWebView代替了UIWebView戳粒,但是WKWebView并不支持方法二。此時我們可以使用WKWebView的WKScriptMessageHandler
<1>初始化WKWebView時虫啥,調(diào)用addScriptMessageHandler:name:方法蔚约,name為js中的方法名,如scan:
- (void)setupWKWebView{
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [[WKUserContentController alloc] init];
[configuration.userContentController addScriptMessageHandler:self name:@"scan"];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
webView.UIDelegate = self;
}
h5:
window.webkit.messageHandlers.scan.postMessage()
<2>實現(xiàn)WKScriptMessageHandler代理方法涂籽,當(dāng)js調(diào)用scan方法時苹祟,會回調(diào)此代理方法:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
if ([message.name isEqualToString:@"scan"]) {
//調(diào)用原生掃碼
}
}
實現(xiàn)原理:
1、JS與iOS約定好scan方法评雌,用作JS在調(diào)用iOS時的方法树枫;
2、iOS使用WKUserContentController的-addScriptMessageHandler:name:方法監(jiān)聽name為scan的消息景东;
3砂轻、JS通過window.webkit.messageHandlers.jsToOc.postMessage()的方式對scan方法發(fā)送消息;
4斤吐、iOS在-userContentController:didReceiveScriptMessage:方法中讀取name為scan的消息數(shù)據(jù)message.body
PS:[userContentController addScriptMessageHandler:self name:@"scan"]會引起循環(huán)引用問題搔涝。一般來說,在合適的時機(jī)removeScriptMessageHandler可以解決此問題和措。比如:在-viewWillAppear:方法中執(zhí)行add操作庄呈,在-viewWillDisappear:方法中執(zhí)行remove操作。如下:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"scan"];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"scan"];
}
方法四 WebViewJavascriptBridge
是一個第三方框架, 官方文檔和demo都很完整, 不再累贅, GitHub地址:
https://github.com/marcuswestin/WebViewJavascriptBridge