WKWebKit与JavaScript交互

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注册一个名称为TDWebKitTDMessageHandler对象

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]);
       }
                        
 }];

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容