美文网首页
Vue - Vue与WKWebView互相通信(OC与Vue相互

Vue - Vue与WKWebView互相通信(OC与Vue相互

作者: 西半球_ | 来源:发表于2021-05-31 15:04 被阅读0次

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>

相关文章

网友评论

      本文标题:Vue - Vue与WKWebView互相通信(OC与Vue相互

      本文链接:https://www.haomeiwen.com/subject/mercsltx.html