demo 地址: https://github.com/iotjin/JhVueOCDemo
原生:
使用WKWebView替换UIWebView
iOS下OC与JS的交互(WKWebview-MessageHandler实现)
iOS WKWebView与JS交互)
三方库:
WebViewJavaScriptBridge 基本使用
iOS-WKWebView与JavaScript交互的简单使用
一. OC调用JS(往JS传值)
通过
-evaluateJavaScript:completionHandler:
实现OC调用JS,跟JavaScriptCore中的evaluateScript
方法类似。
WKWebView本身提供一个方法进行处理JS代码.
javaScriptString:所执行的JS代码
completionHandler:回调
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^
_Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
关键代码:
vue
methods: {
//接收原生端传递过来的数据
receiveAPPData(params) {
alert(params);
console.log("接收的数据:" + params);
this.$refs.text.innerText = "接收的数据:" + params; //更新值
},
},
mounted() {
//Vue的方法给原生调用前,需要把方法挂在Window下面
window.receiveAPPData = this.receiveAPPData;
},
OC
NSString *params = @"2222";
NSString *jsStr = [NSString stringWithFormat:@"receiveAPPData('%@')",params];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@" response %@ ",response);
NSLog(@" error %@ ",error);
}];
二. JS调用OC(往OC传值)
当JS端想传一些数据给iOS,那它们会调用下方方法来发送.
window.webkit.messageHandlers.<对象名>.postMessage(<数据>);
关键点:
JS往iOS传递消息,iOS需要先注册JS消息,name必须JS发送消息时的名字一致
然后JS发送信息:window.webkit.messageHandlers.<对象名>.postMessage(<数据>);
内存泄露处理
在viewWillDisappear
方法中调用removeScriptMessageHandlerForName
方法
关键代码:
vue
methods: {
btnClick() {
console.log("点击了按钮");
/* 往原生端发送数据,iOS注册的消息名字要保持一致
SendDataToApp是iOS的方法
messageHandlers在电脑浏览器上可能会报错,但是在iOS的webkit里面正常,
所以这里书写的时候要注意这个方法尽量写在最后,不然后面的代码不能执行,或使用try...catch
*/
window.webkit.messageHandlers.SendDataToApp.postMessage({
params: "参数",
});
},
},
OC
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
/** JS往iOS传递消息,iOS需要先注册JS消息
然后 JS发送信息: window.webkit.messageHandlers.<对象名>.postMessage(<数据>);
*/
//注册JS消息,name必须JS发送消息时的名字一致
[userContentController addScriptMessageHandler:self name:kSendDataToIOS];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;
WKWebView *webView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:config];
// 注意: 不是在dealloc中移除, 因为已经循环引用了, 不会执行dealloc方法.
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// 视图即将消失的时候, 移除 防止循环引用
// self-->webView-->configuration-->userContentControll-->self 循环引用
if (self.webView) {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:kSendDataToIOS];
}
}
三 全部代码
OC
#import "VueViewController.h"
#import <WebKit/WebKit.h>
#define kSendDataToIOS @"SendDataToApp" //与后台约定的方法名
#define kReceiveAPPData @"receiveAPPData"
@interface VueViewController ()<WKScriptMessageHandler,WKNavigationDelegate,WKUIDelegate>
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation VueViewController
- (void)dealloc {
NSLog(@" VueViewController - dealloc ");
}
// 注意: 不是在dealloc中移除, 因为已经循环引用了, 不会执行dealloc方法.
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// 视图即将消失的时候, 移除 防止循环引用
// self-->webView-->configuration-->userContentControll-->self 循环引用
if (self.webView) {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:kSendDataToIOS];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setNav];
[self configWebView];
}
- (void)setNav {
WeakSelf
self.Jh_navRightTitle = @"往JS传值";
self.JhClickNavRightItemBlock = ^{
[weakSelf eventHandle];
};
}
- (void)configWebView {
[self.view addSubview:self.webView];
NSString *path = @"dist/index.html";
NSURL *url = [[NSBundle mainBundle] URLForResource:path withExtension:nil];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
#pragma mark - 交互处理
- (void)eventHandle {
//OC往js发送信息
NSString *name = kReceiveAPPData;
NSString *params = @"2222";
NSString *jsStr = [NSString stringWithFormat:@"%@('%@')",name,params];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@" response %@ ",response);
NSLog(@" error %@ ",error);
}];
}
//接收js传过来的数据
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"message.name = %@;message.body=%@", message.name,message.body);
if ([message.name isEqualToString:kSendDataToIOS]) {
NSDictionary *jsData = message.body;
}
}
#pragma mark - WKNavigationDelegate
// WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@" 页面开始加载时调用 ");
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@" 页面开始加载时调用 ");
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@" 页面加载完毕时调用 ");
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@" 页面加载失败时调用 ");
}
#pragma mark -- WKUIDelegate
// 提醒 对应js的Alert方法
/**
* web界面中有弹出警告框时调用
*
* @param webView 实现该代理的webview
* @param message 警告框中的内容
* @param frame 主窗口
* @param completionHandler 警告框消失调用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@" message %@ ",message);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Vue接收的数据:" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
// 确认提交 对应js的confirm方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
// 按钮
UIAlertAction *alertActionCancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// 返回用户选择的信息
completionHandler(NO);
}];
UIAlertAction *alertActionOK = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
// alert弹出框
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:alertActionCancel];
[alertController addAction:alertActionOK];
[self presentViewController:alertController animated:YES completion:nil];
}
// 文本框输入 对应js的prompt方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
// alert弹出框
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert];
// 输入框
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = defaultText;
}];
// 确定按钮
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// 返回用户输入的信息
UITextField *textField = alertController.textFields.firstObject;
completionHandler(textField.text);
}]];
// 显示
[self presentViewController:alertController animated:YES completion:nil];
}
- (WKWebView *)webView {
if (!_webView) {
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
/** JS往iOS传递消息,iOS需要先注册JS消息
然后 JS发送信息: window.webkit.messageHandlers.<对象名>.postMessage(<数据>);
*/
//注册JS消息,name必须JS发送消息时的名字一致
[userContentController addScriptMessageHandler:self name:kSendDataToIOS];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentController;
WKWebView *webView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:config];
webView.navigationDelegate = self;
webView.UIDelegate = self;
_webView = webView;
}
return _webView;
}
@end
vue
<template>
<div class="about">
<div class="btn" @click="btnClick">往原生端传值</div>
<div class="text" ref="text">接收的数据:</div>
</div>
</template>
<script>
export default {
data: function() {
return {
title: "标题",
};
},
methods: {
btnClick() {
console.log("点击了按钮");
/* 往原生端发送数据,iOS注册的消息名字要保持一致
SendDataToApp是iOS的方法
messageHandlers在电脑浏览器上可能会报错,但是在iOS的webkit里面正常,
所以这里书写的时候要注意这个方法尽量写在最后,不然后面的代码不能执行,或使用try...catch
*/
window.webkit.messageHandlers.SendDataToApp.postMessage({
params: "参数",
});
},
//接收原生端传递过来的数据
receiveAPPData(params) {
alert(params);
console.log("接收的数据:" + params);
this.$refs.text.innerText = "接收的数据:" + params; //更新值
},
},
mounted() {
//Vue的方法给原生调用前,需要把方法挂在Window下面
window.receiveAPPData = this.receiveAPPData;
},
};
</script>
<style>
#btn {
background: yellow;
width: 200px;
height: 50px;
}
</style>
网友评论