iOS js和OC交互

序言:

在iOS开发中,已经多次与js进行交互。这里系统的做一下整理,着重从三个方面进行阐述:

  • UIWebView和WKWebView的对比
  • 原生与js互相调用的几种方案
  • 开发中遇到的一些坑

正文:

UIWebView(iOS2)和WKWebView(iOS8)的对比:

1.内存占用是UIWebView的1/4~1/3 ;
2.页面加载速度有提升,有的文章说它的加载速度比UIWebView提升了一倍左右;
3.更为细致地拆分了 UIWebViewDelegate 中的方法;
4.自带进度条。不需要像UIWebView一样自己做假进度条(通过NJKWebViewProgress和双层代理技术实现),技术复杂度和代码量,根贴近实际加载进度优化好的多;
5.允许JavaScript的Nitro库加载并使用(UIWebView中限制);
6.可以和js直接互调函数,不像UIWebView需要第三方库;WebViewJavascriptBridge来协助处理和js的交互;
7.不支持页面缓存,需要自己注入cookie,而UIWebView是自动注入cookie;
8.无法发送POST参数问题。

原生与js互相调用的几种方案:
UIWebView与JS的交互方式
  1. 原生调用js
  • OC代码:
[self.webView stringByEvaluatingJavaScriptFromString:@"add(1,2)"];
  • js代码:
function add(a,b) {
    return a+b;
}
  1. js调用原生
    方案一 iOS与JS交互之UIWebView-协议拦截
  • OC代码:
//让Native 代码拦截, 
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest:)request navigationType:(UIWebViewNavigationType)navigationType{
    NSString *schem = webView.request.URL.scheme;
    if ([schem containsString:@"jsCallBack://"]) {
        //action...
        return NO;
    }
}
  • js代码:
function btnClick1() {
    location.href = "jsCallBack://method_?param1¶m2"
}

方案二 UIWebView-JavaScriptCore

  • OC代码:
//! 导入JavaScriptCore框架头文件
#import 

#pragma mark - UIWebViewDelegate
//! UIWebView在每次加载请求完成后会调用此方法
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    
    //! 获取JS代码的执行环境/上下文/作用域
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    //! 监听JS代码里面的jsToOc方法(执行效果上可以理解成重写了JS的jsToOc方法)
    context[@"jsToOc"] = ^(NSString *action, NSString *params) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [UIWebViewJavaScriptCoreController showAlertWithTitle:action message:params cancelHandler:nil];
        });
    };
}
  • js代码:
    同下面WKWebView中js调用OC里的 方法二 的js代码一样。

方案三 UIWebView-JSExport协议

  • OC代码:
//! 导入JavaScriptCore框架头文件
#import 

@protocol OCJSExport 

//! 为OC的-jsToOC:params:方法起个JS认识的别名jsToOc
JSExportAs(jsToOc, - (void)jsToOc:(NSString *)action params:(NSString *)params);

@end

//! UIWebViewJSExportController遵守OCJSExport协议
@interface UIWebViewJSExportController () 
#pragma mark - JSExport functions

//! 实现OCJSExport协议的方法
- (void)jsToOc:(NSString *)action params:(NSString *)params {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIWebViewJSExportController showAlertWithTitle:action message:params cancelHandler:nil];
    });
}
#pragma mark - UIWebViewDelegate

//! UIWebView在每次加载请求完成后会调用此方法
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    
    //! 获取JS代码的执行环境/上下文/作用域
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    //! 在context注册OCJSBridge对象为self
    context[@"OCJSBridge"] = self;//!< 有循环引用问题
}
  • js代码:
//! 登录
function login() {
  var token = "js_tokenString";
  loginSucceed(token);
}

//! 登录成功
function loginSucceed(token) {
  var action = "loginSucceed";
  OCJSBridge.jsToOc(action, token);
}
WKWebView与JS的交互方式
  1. 原生调用js
  • OC代码:
[self.wkWebView evaluateJavaScript:@"playSount()" completionHandler:nil];
  • js代码:
function playSount() {
    //playSount...
}
  1. js调用原生

方案一: WKWebView-WKScriptMessageHandler

  • OC代码:
    1)在创建wkWebView时,需要将被js调用的方法注册进去
//创建WKWebViewConfiguration文件
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.preferences.minimumFontSize = 10.f;
    [config.userContentController addScriptMessageHandler:self name:@"playSound"];
//创建WKWebView类
    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];

2)在WKScriptMessageHandler代理方法中监听js的调用

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"playSound"]) {
        [self playSound];
    }
}
  • js代码:
//JS响应事件
function btnClick() {
    window.webkit.messageHandlers.playSound.postMessage(null);
}

//js各种情况代码示例:
// 空的时候null 啥都不写的话会有问题
function scanClick() {
window.webkit.messageHandlers.ScanAction.postMessage(null);
}
// 传字典
function shareClick() {
window.webkit.messageHandlers.Share.postMessage({title:'测试分享的标题',content:'测试分享的内容',url:'http://www.baidu.com'});
}
// 传字符串
function playSound() {
window.webkit.messageHandlers.PlaySound.postMessage('shake_sound_male.wav');
}
// 传数组
function colorClick() {
window.webkit.messageHandlers.Color.postMessage([67,205,128,0.5]);
}

方案二: WKWebView-协议拦截

  • OC代码:
#pragma mark - WKNavigationDelegate
//! WKWeView在每次加载请求前会调用此方法来确认是否进行请求跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    if ([navigationAction.request.URL.scheme caseInsensitiveCompare:@"jsToOc"] == NSOrderedSame) {
        [WKWebViewInterceptController showAlertWithTitle:navigationAction.request.URL.host message:navigationAction.request.URL.query cancelHandler:nil];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}
  • js代码:
//! 登录
function login() {
  var token = "js_tokenString";
  loginSucceed(token);
}

//! 登录成功
function loginSucceed(token) {
  var action = "loginSucceed";
  jsToOc(action, token);
}

//! JS调用OC入口
function jsToOc(action, params) {
  var url = "jsToOc://" + action + "?" + params;
  loadURL(url);
}

//! 加载URL
function loadURL(url) {
  window.location.href = url;
}

UIWebView 默认会显示js弹窗,WKWebView要想显示js弹窗的话,需要实现下面代理方法:

#pragma mark - WKUIDelegate

//! alert(message)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    [alertController addAction:cancelAction];
    [self presentViewController:alertController animated:YES completion:nil];
}

//! confirm(message)
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Confirm" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }];
    UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }];
    [alertController addAction:cancelAction];
    [alertController addAction:confirmAction];
    [self presentViewController:alertController animated:YES completion:nil];
}

//! prompt(prompt, defaultText)
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler {
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = defaultText;
    }];
    UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text);
    }];
    [alertController addAction:confirmAction];
    [self presentViewController:alertController animated:YES completion:nil];
}

这里还有一个三方WebViewJavascriptBridge,UIWebView和WKWebVIew都可以用,用起来也是非常的方便

UIWebView与JS交互遇到的一些坑
  • iOS9白屏问题 解决方案
  • WKWebView 存储cookie,和UIWebView是不一样的 cookie相关

结束语:
望见伊人,在水一方
我顺流而下,或逆流而上
不畏三千里河长,不惧万重山阻挡

参考文章:
iOS与JS交互之WKWebView-协议拦截
iOS UIWebView 与WKWebView集锦

你可能感兴趣的:(iOS js和OC交互)