JavaScript与 OC 的交互使用

如果没有接触过 JavaScript 的可以在这里看看JavaScript菜鸟教程,我也是在上面学习的JavaScript
在这里以WKWebView为主。

第一步,创建WKWebView并配置:

  • 导入头文件
    #import
    #import
  • delegate

  • WKWebView 对象
- (WKWebView *)wkWebview
{
    if (_wkWebview == nil)
    {
        
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        //TODO:设置偏好设置
        config.preferences = [[WKPreferences alloc] init];
        //1-默认为0
        config.preferences.minimumFontSize = 80;
        //2-JavaScript 是否可用,默认认为YES
        config.preferences.javaScriptEnabled = YES;
        //3-在iOS上默认为NO,表示不能自动通过窗口打开
        config.preferences.javaScriptCanOpenWindowsAutomatically = YES;
        config.processPool = [[WKProcessPool alloc] init];
        config.userContentController = [[WKUserContentController alloc] init];
        

    

        _wkWebview = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, HEIGHT==812?(HEIGHT - 34):HEIGHT)
                                       configuration:config];
        _wkWebview.UIDelegate = self;
        _wkWebview.navigationDelegate = self;
        _wkWebview.backgroundColor = [UIColor clearColor];
        _wkWebview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        _wkWebview.multipleTouchEnabled = YES;
        _wkWebview.autoresizesSubviews = YES;
        _wkWebview.scrollView.alwaysBounceVertical = YES;
        _wkWebview.allowsBackForwardNavigationGestures = YES;/**这一步是,开启侧滑返回上一历史界面**/
        [self.view addSubview:_wkWebview];
    }
    return _wkWebview;
}

第二步、监听网页上的弹窗事件并回传信息

JavaScript弹窗分为三种,alert警告框confirm确认框prompt提示窗。在 Objective-C中分别有对应的方法来接收和响应他们的操作并且回传需要的信息。

alert警告框
#pragma mark 接收网页端过来的 alert 信息
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    [alert addAction:confirm];
    [self presentViewController:alert animated:YES completion:nil];
    completionHandler();
}
confirm确认框
#pragma mark 接收确认框信息,并提交操作
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }];
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }];
    
    [alert addAction:confirm];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}
prompt提示窗
#pragma mark 接收网页端 prompt 信息,并提交输入内容
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
    [alert  addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        
    }];
    UIAlertAction *confirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alert.textFields[0].text);
    }];
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(@"");
    }];
    
    [alert addAction:confirm];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}

第三步、响应其它网页端事件,接收JavaScript传递过来的值。

如下,html 中有一个 button 元素,它响应的JavaScript函数为eoc()




如何在OC端响应这个消息呢?

  • 我们可以采取类似OC中观察者模式的方式,在eoc()中添加 window.webkit.messageHandlers.htmlAction01.postMessage('点击了第一个按钮');来向OC发送一条信息,具体的格式为:
    window.webkit.messageHandlers.ActionObjectName.postMessage('所需传递的值或信息');
  • OC中通过WKWebView添加消息监听,具体为
//4-在这里设置监听代理,监听 JavaScript 对象的发出的信息
[config.userContentController addScriptMessageHandler:self name:@"htmlAction01"];
[config.userContentController addScriptMessageHandler:self name:@"htmlAction02"];
[config.userContentController addScriptMessageHandler:self name:@"htmlAction03"];
  • 在代理方法中做出操作
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([message.name isEqualToString:@"htmlAction01"]) {
        NSLog(@"*01***%@***",message);
    }else if ([message.name isEqualToString:@"htmlAction02"]){
        NSLog(@"*02***%@***",message);
    }else if ([message.name isEqualToString:@"htmlAction03"]){
        NSLog(@"*03***%@***",message);
    }
        [self alertWithMessage:message.body];
}

第四步、JavaScript响应OC的消息,接收传递过来的值

  • 添加新元素节点
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

具体的使用:

NSString *jsValue = @"var ne = document.createElement('button');
                      var nc = document.createTextNode('后来添加的button');
                      ne.addEventListener('click',function(){alert('这是新添加 btn 的测试弹窗')});
                      ne.appendChild(nc);
                      document.getElementById('bby').appendChild(ne);"
[webView evaluateJavaScript:jsValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        NSLog(@"*001***** %@ *****error:%@",response,error);
    }];

上面的操作会找到idbby的元素,并向这个元素节点之中添加一个button元素,同时这个button元素会响应function(){alert('这是新添加 btn 的测试弹窗')这个操作;

  • 传值给JavaScript

方式一:
在网页端有这样一个函数

function userLogin(isLogin){
    alert(isLogin?"用户已登录":"用户未登录");
}

OC端的操作

 NSString *jsValue = [NSString stringWithFormat:@"userLogin(%d)",YES];
 [self.wkWebview evaluateJavaScript: jsValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
     NSLog(@"*002***** %@ *****error:%@",response,error);
}];

  • 方式二
NSString *jsValue = @"confirm('{name:Tom,age:18,height:188}');";
 [self.wkWebview evaluateJavaScript: jsValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
    NSLog(@"*003***** %@ *****error:%@",response,error);
}];

不足的地方,还请多多指教,谢谢了。



更新:经过一位大神同学指导,
关于JavaScript调用 原生方法
因为window.webkit.messageHandlers.ActionObjectName.postMessage('所需传递的值或信息');只在第一次注册时有效,且可能会随着代码量的增加要注册很多个,这样就麻烦一些了。
所以,当我们想通过JavaScript调用 原生方法且需要回传值时,可以在JavaScript端对prompt的参数进行封装(大神同学采取的是对prompt封装,我暂时没有看懂他写的,就对prompt的参数进行了封装),这样就能在不注册window.webkit.messageHandlers.ActionObjectName.postMessage('所需传递的值或信息');的时候随时调用 原生方法。
思路:

JavaScript

function bridge(nativeMethod,otherParams,isNativeMethod,promptStr){
        var txt = {
            'nativeMethod':nativeMethod,
            'otherParams':otherParams,
            'isNativeMethod':isNativeMethod,
            'promptStr':promptStr
        };
        console.log(JSON.stringify(txt));
        return prompt(JSON.stringify(txt),'');
    }

Objective-C
在原生端,可以根据isNativeMethod参数来判断是调用原生方法,还是弹起弹窗。
nativeMethod参数是所要调用原生方法的方法名字符串,通过NSSelectorFromString(<#NSString * _Nonnull aSelectorName#>)转为为原生方法。
otherParams是其它需要传递的参数。
promptStr是弹窗的提示信息。

你可能感兴趣的:(JavaScript与 OC 的交互使用)