JS调用OC的方法
以下代码都在这个demo里。可以通过下面几种方式实现js调用oc。
1.拦截URL
WKWebview可以在代理方法里拦截假的URL,从URL中获取要调用的方法名字和参数。
html的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<title>分享页</title>
</head>
<body>
<p></p>
<div>
<button onclick="showToast();">无参数弹框提示</button>
<button onclick="showToastWithParameter();">有参数弹框提示</button>
<button onclick="shareClick();">Click Me!</button>
</div>
<p></p>
<script>
function showToast() {
window.location.href = 'test1://showToast';
}
function showToastWithParameter() {
window.location.href = 'test1://showToastWithParameter?parama=666666';
}
//不使用window.location.href
function loadURL(url) {
var iFrame;
iFrame = document.createElement("iframe");
iFrame.setAttribute("src", url);
iFrame.setAttribute("style", "display:none;");
iFrame.setAttribute("height", "0px");
iFrame.setAttribute("width", "0px");
iFrame.setAttribute("frameborder", "0");
document.body.appendChild(iFrame);
// 发起请求后这个 iFrame 就没用了,所以把它从 dom 上移除掉
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
}
function shareClick() {
loadURL("Test2://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址");
}
</script>
</body>
</html>
当用户点击按钮时,会在WKWebview
的代理方法:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSURLRequest *request = [navigationAction request];
NSString * scheme = request.URL.scheme;
NSString * host = request.URL.host;
NSString * query = request.URL.query;
if ([scheme isEqualToString:@"test1"]) {
NSString *methodName = host;
if (query) {
methodName = [methodName stringByAppendingString:@":"];
}
SEL sel = NSSelectorFromString(methodName);
NSString *parameter = [[query componentsSeparatedByString:@"="] lastObject];
[self performSelector:sel withObject:parameter];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}else if ([scheme isEqualToString:@"test2"]){//JS中的是Test2,在拦截到的url scheme全都被转化为小写。
NSURL *url = request.URL;
NSArray *params =[url.query componentsSeparatedByString:@"&"];
NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
for (NSString *paramStr in params) {
NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
if (dicArray.count > 1) {
NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[tempDic setObject:decodeValue forKey:dicArray[0]];
}
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"方式一" message:@"这是OC原生的弹出窗" delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
[alertView show];
NSLog(@"tempDic:%@",tempDic);
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
2.scriptMessageHandler
这是Apple在WebKit
里新增加的方法,位于WKUserContentController
类里。
/*! @abstract Adds a script message handler.
@param scriptMessageHandler The message handler to add.
@param name The name of the message handler.
@discussion Adding a scriptMessageHandler adds a function
window.webkit.messageHandlers.<name>.postMessage(<messageBody>) for all
frames.
*/
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
/*! @abstract Removes a script message handler.
@param name The name of the message handler to remove.
*/
- (void)removeScriptMessageHandlerForName:(NSString *)name;
第一个方法会在所有的frame里添加一个js的函数window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
。比如,我们在OC中添加一个handle:
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"shareNothing"]; ;
当我们在js中调用下面方法时:
window.webkit.messageHandlers.shareNothing.postMessage(null);//null必须要写
我们在OC中会收到WKScriptMessageHandler
的回调:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"shareNothing"]) {
}
}
当然,记得在适当的地方调用removeScriptMessageHandler:
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareNothing"];
html代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<title>分享页</title>
</head>
<body>
<p></p>
<div>
<button onclick="share('分享标题', 'http://cc.cocimg.com/api/uploads/170425/b2d6e7ea5b3172e6c39120b7bfd662fb.jpg', location.href)">分享</button>
<button onclick="test()">分享不带参数</button>
</div>
<p></p>
<script>
function share (title, imgUrl, link) {
//便于WKWebView测试
window.webkit.messageHandlers.shareTitle.postMessage({aTitle: title, aImgUrl: imgUrl, aLink: link});
//这里需要OC实现
}
function test() {
//便于WKWebView测试
window.webkit.messageHandlers.shareNothing.postMessage(null);//null必须要写
}
</script>
</body>
</html>
OC代码:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"shareTitle"];
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"shareNothing"];
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareTitle"];
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareNothing"];
}
#pragma mark -- WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSString *name = message.name;
id body = message.body;
NSLog(@"%@", message.name);
NSLog(@"%@", message.body);
if ([name isEqualToString:@"shareTitle"]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
if ([name isEqualToString:@"shareNothing"]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
}
WebViewJavascriptBridge
WebViewJavascriptBridge
是一个第三方的库用于和js交互的。它在做WKWebView的js调用oc方法的时候使用的是拦截假的url的方式。
DSBridge
DSBridge在做js调用oc的方法的时候,没有使用拦截URL和Message handle的方式。它的js方法会调用到ret = prompt("_dsbridge=" + method, arg);
,这是js的一个弹框的方法,会调用到WKWebView的代理方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
在这个方法里再去调用oc的方法。
JS对象:
html代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script>
function test(paramete){
JSObject.share(paramete);
}
</script>
</head>
<body>
<button onclick="test('1')" style="width: 80px;height: 35px;">分享</button>
<div id='zsz'></div>
</body>
</html>
html中用到了JSObject,这里oc里要添加自定义的脚本:
/**
* Native为H5提供的Api接口
*
* @type {js对象}
*/
var JSObject = (function() {
var NativeApi = {
share: function(param) {
//调用native端
_nativeShare(param);
},
}
function _nativeShare(param) {
//js -> oc
window.webkit.messageHandlers.shareTitle.postMessage(param);
}
//闭包,把Api对象返回
return NativeApi;
})();
OC的代码:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self addNativeApiToJS];
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"shareTitle"];
}
- (void)addNativeApiToJS
{
//防止频繁IO操作,造成性能影响
static NSString *nativejsSource;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
nativejsSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"NativeApi" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];
});
//添加自定义的脚本
WKUserScript *js = [[WKUserScript alloc] initWithSource:nativejsSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[self.webView.configuration.userContentController addUserScript:js];
//注册回调
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"shareTitle"];
}
#pragma mark -- WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSString *name = message.name;
id body = message.body;
NSLog(@"%@", message.name);
NSLog(@"%@", message.body);
if ([name isEqualToString:@"shareTitle"]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS调用OC代码成功!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
}