WKWebView与JS交互

前言

经常看到安卓开发者前端联调,前端一句window.android.方法名(参数)这样的函数就调用了安卓方法,而ios确非常的麻烦,以前一般我们都是通过在- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler中拦截url处理。现在,下面介绍另一种WKScriptMessageHandler方法实现,与安卓类似。


ios端处理

  • 使用WKWebView,遵循WKScriptMessageHandler协议
  • 创建WKWebView
// oc
- (WKWebView *)wkWebView {
    if (!_wkWebView) {
        
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        WKPreferences *preference = [[WKPreferences alloc] init];
        config.preferences = preference;
        
        _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:config];
        
        _wkWebView.UIDelegate = self;
        _wkWebView.navigationDelegate = self;
    
    }
    return _wkWebView;
}
// swift
// MARK: 懒加载
lazy var webView: WKWebView = {
        
    let config = WKWebViewConfiguration.init()
    let preference = WKPreferences.init()
    preference.javaScriptCanOpenWindowsAutomatically = true
    config.preferences = preference
        
    let webV = WKWebView.init(frame: CGRect(x: 0, y:20, width: screenW, height: screenH-navH), configuration: config)
    webV.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    webV.scrollView.scrollIndicatorInsets = webV.scrollView.contentInset
    webV.uiDelegate = self
    webV.navigationDelegate = self
    return webV
}()
  • 注册监听与前端约好的方法名
// oc
- (void)viewWillAppear:(BOOL)animated {
    
    [super viewWillAppear:animated];
    
    [self.wkWebView.configuration.userContentController addScriptMessageHandler:self name:@"goHome"];
    [self.wkWebView.configuration.userContentController addScriptMessageHandler:self name:@"goLogin"];
    [self.wkWebView.configuration.userContentController addScriptMessageHandler:self name:@"buyLottery"];
    
}
// swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.webView.configuration.userContentController.add(self, name: "goHome")
    self.webView.configuration.userContentController.add(self, name: "goLogin")
    self.webView.configuration.userContentController.add(self, name: "buyLottery")
}
  • 在页面消失时,记得移除监听
//oc
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"goHome"];
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"goLogin"];
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"buyLottery"];
    self.wkWebView.UIDelegate = nil;
    self.wkWebView.navigationDelegate = nil;
}

//swift
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.webView.configuration.userContentController.removeScriptMessageHandler(forName: "goHome")
    self.webView.configuration.userContentController.removeScriptMessageHandler(forName: "goLogin")
    self.webView.configuration.userContentController.removeScriptMessageHandler(forName: "buyLottery")
    self.webView.uiDelegate = nil
    self.webView.navigationDelegate = nil
}
  • 代理方法中监听前端调用原生的方法
//oc
/// 与js交互
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
    // 参数
    NSDictionary *body = message.body;
    // 约定好的函数名
    NSString *name = message.name;

    if ([name isEqualToString:@"goHome"]) {
        [self.navigationController popViewControllerAnimated:true];
    } else if ([name isEqualToString:@"goLogin"]) {
        [self.navigationController popViewControllerAnimated:true];
        [self.webObj.webViewEngine evaluateScript:@"window.location.href = \"login.html\";localStorage.clear();"];
    } else if ([name isEqualToString:@"buyLottery"]) {
        [[EUExPrintSdk sharedInstance] printBlueTooth:@[body]];
    }
}
// swift
extension XMBaseWebVC: WKScriptMessageHandler {

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        
        let body = message.body as? [String:Any]
        let name = message.name
        XMLog(message: "messageHandler收到的名字为:"+name)
        switch name {
            case "goHome":
                self.goHome()
            case "goLogin":
                let url = body?["shareUrl"] as? String
                let pasteBoard = UIPasteboard.general
                pasteBoard.string = url
                XMMessageHUD.showMessage(str: NSLocalizedString("复制成功", comment: ""), completion: nil)
            case "buyLottery":
                let url = body?["url"] as? String
                let title = body?["title"] as? String
                let content = body?["description"] as? String
                let pic = body?["pic"] as? String
                XMLog(message: "........")
            default:
                break
        }
    }
}

前端js调用

注意:一定要参传数:NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull,不然原生是收不到消息
如:

window.webkit.messageHandlers.goHome.postMessage(null); // 回到首页操作
window.webkit.messageHandlers.goHome.postMessage("哈哈"); // 回到首页操作
window.webkit.messageHandlers.goLogin.postMessage("哈哈"); // 跳转到登录面
window.webkit.messageHandlers.buyLottery.postMessage({"mount":"145","oderid":"6d-3891489374893473243","count":"12"}); // 执行买票操作,参数是通过json方法传送,原生是以body方式接收,接收到的是字典