WKWebKit与JavaScript交互
JS调用Native
WKWebKit支持JavaScript的3个弹窗函数,WKUIDelegate提供3个代理方法与之对应处理
- alert()
js示例代码
alert('你的操作有误')
WKUIDelegate代理方法:
- (void)webView:(WKWebView *)webView
runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
//message == “你的操作有误”
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:
UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();//用户点击“确定”通知js回调
}]];
[self presentViewController:alert animated:YES completion:NULL];
}
- confirm()
js示例代码
var userOperation = confirm('你确定要删除吗?')
//confirm()的返回值是一个布尔值
WKUIDelegate代理方法:
- (void)webView:(WKWebView *)webView
runJavaScriptConfirmPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(BOOL result))completionHandler
{
//message == "你确定要删除吗?"
UIAlertController *alert = [UIAlertController alertControllerWithTitle:
@"警告" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"是"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
completionHandler(YES);//回调给js,通知用户点击了“是”。
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"否"
style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);//回调给js,通知用户点击了“否”。
}]];
[self presentViewController:alert animated:YES completion:NULL];
}
- prompt()
js示例代码
var text = prompt('提示','请输入你的真实姓名')
//prompt()的返回值是一个字符串
WKUIDelegate代理方法:
- (void)webView:(WKWebView *)webView
runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
defaultText:(nullable NSString *)defaultText
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(NSString * _Nullable result))completionHandler
{
//prompt == "提示", defaultText == "请输入你的真实姓名"
UIAlertController *alert = [UIAlertController alertControllerWithTitle:
prompt message:defaultText
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.textColor = [UIColor redColor];
}];
[alert addAction:[UIAlertAction actionWithTitle:@"完成"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler([[alert.textFields lastObject] text]);
//用户输入的文本回调给js
}]];
[self presentViewController:alert animated:YES completion:NULL];
}
JavaScript调用Navtive自定义类的接口
WKWebView中的js runtime里事先注入了一个window.webkit.messageHandlers.{NAME}.postMessage()
方法,我们可以使用这个方法直接向Native层传一个消息对象。其中{NAME}
是一个WKScriptMessageHandler
对象的名称,WKWebView事先注册这个名称,Native接收到JS传来的消息,可以用这个名称进行区别处理。
JS给Native发送消息对象
var message = {'methodName':methodName,'params':params,'callBackName':callBackName};
window.webkit.messageHandlers.TDWebKit.postMessage(message);
//TDWebKit是iOS客户端与web端约定好的一个消息处理对象名称
//message对象具体字段也需要客户端与web端约定好
js如果需要得到客户端返回的消息,需要一个写一个callBack回调函数
callBackName:function(data){
//to do something...
},
客户端创建一个WKScriptMessageHandler
对象来处理JS发过来的消息对象
TDMessageHandler.h
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
#import "FMMacros.h"
extern NSString *const TDWebKit;
@interface TDMessageHandler : NSObject<WKScriptMessageHandler>
SINGLETON_DECLARE()
- (void)joinWebView:(WKWebView *)webView;
@end
TDMessageHandler.m
#import "TDMessageHandler.h"
NSString *const TDWebKit = @"TDWebKit";
@interface TDMessageHandler ()
@property (nonatomic, weak) WKWebView *webView;
@end
@implementation TDMessageHandler
SINGLETON_IMPL(TDMessageHandler)
- (void)joinWebView:(WKWebView *)webView {
self.webView = webView;
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:TDWebKit]) {
NSString *methodName = message.body[@"methodName"];
NSDictionary *params = message.body[@"params"];
NSString *callBackName = message.body[@"callBackName"];
if (callBackName) {
WeakSelf()
[self interactWitMethodName:methodName params:params callBack:^(id response) {
//native调用js,web端需要有一个回调函数callBackName(response);
NSString *js = [NSString stringWithFormat:@"%@('%@');",callBackName,response];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.webView evaluateJavaScript:js completionHandler:^(id _Nullable data, NSError * _Nullable error) {
if (error) {
NSLog(@"native调用js失败: %@",[error localizedDescription]);
}
}];
});
}];
}
else{
[self interactWitMethodName:methodName params:params callBack:nil];
}
}
}
- (void)interactWitMethodName:(NSString *)methodName params:(NSDictionary *)params callBack:(void(^)(id response))callBack{
if (params) {
methodName = [methodName stringByAppendingString:@":"];
if (callBack) {
methodName = [methodName stringByAppendingString:@"callBack:"];
SEL selector = NSSelectorFromString(methodName);
NSArray *paramArray =@[params,callBack];
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObjects:paramArray];
}
}
else {
SEL selector = NSSelectorFromString(methodName);
NSArray *paramArray =@[params];
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObjects:paramArray];
}
}
}
else{
if (callBack) {
methodName = [methodName stringByAppendingString:@":"];
SEL selector = NSSelectorFromString(methodName);
NSArray *paramArray =@[callBack];
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObjects:paramArray];
}
}
else {
SEL selector = NSSelectorFromString(methodName);
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObjects:nil];
}
}
}
}
- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];
NSUInteger i = 1;
for (id object in objects) {
id tempObject = object;
[invocation setArgument:&tempObject atIndex:++i];
}
[invocation invoke];
if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}
@end
这里建一个TDMessageHandler的分类,在分类里提供JS发送消息处理的接口
TDMessageHandler+WebKit.h
#import "TDMessageHandler.h"
@interface TDMessageHandler (WebKit)
- (void)todo;
- (void)share:(NSDictionary *)params callBack:(void(^)(BOOL success))callBack;
- (void)getPhoto:(void(^)(UIImage *image))callBack;
@end
TDMessageHandler+WebKit.m
#import "TDMessageHandler+WebKit.h"
@implementation TDMessageHandler (WebKit)
- (void)todo {
NSLog(@"to do something...");
}
- (void)share:(NSDictionary *)params callBack:(void(^)(BOOL success))callBack {
//params 分享所需要的参数
if (callBack) {
//分享是否成功回调给JS
callBack(true);
}
}
- (void)getPhoto:(void(^)(UIImage *image))callBack {
//TODO 调用系统相机或者相册
if (callBack) {
//选出的相片回调给JS
callBack([UIImage new]);
}
}
@end
WKWebView注册一个名称为TDWebKit
的TDMessageHandler
对象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
[config.userContentController addScriptMessageHandler:[TDMessageHandler sharedInstance] name:TDWebKit];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
Native调用JS
WKWebView可以事先注入一段JS
NSString JSString = @"js string...";
WKUserScript *usrScript = [[WKUserScript alloc] initWithSource:JSString injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
[config.userContentController addUserScript:usrScript];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
Native调用JS
[self.webView evaluateJavaScript:@"js.aFunction()" completionHandler:^(id _Nullable data, NSError * _Nullable error) {
if (error) {
NSLog(@"native调用js失败: %@",[error localizedDescription]);
}
}];