iOS | OC与JS交互

288 阅读4分钟

OC与JS交互,总结就是下面的图:

OC与JS交互总结.png

Demo演示地址: github.com/tanghaitao/…

  • webView底层是 http 2.0 12.0 性能不佳
  • WK 底层是webKit 苹果内核 操作要多一些配置, 性能更好

1. WebView

Demo演示地址: github.com/tanghaitao/…

 // webView底层是 http 2.0 12.0 性能不佳   WK 底层是webKit 苹果内核 操作要多一些 配置
  self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    self.webView.delegate = self;
    [self.view addSubview:self.webView];
  NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
    NSURLRequest *reqeust = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:reqeust];
    
//    NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
//    NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//    [self.webView loadHTMLString:htmlString baseURL:nil];

image.png

执行顺序:

     shouldStartLoadWithRequest   // 控制是否加载
 ****************华丽的分界线****************
     开始加载咯!!!! //webViewDidStartLoad
 ****************华丽的分界线****************
     加载完成了咯!!!! // webViewDidFinishLoad
// 开始加载
- (void)webViewDidStartLoad:(UIWebView *)webView{
  //可以加载progress,这里因为html还没加载完成,所以不能获取html的元素
}
// 加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];// 放到上面的代理函数则没有效果
    self.title = title;
  }
// 加载所有请求数据,以及控制是否加载
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    //navigationType JS响应的样式 点击,提交,返回,刷新,重复提交,其他等都可以拦截到.
    
    // request : host + 路由  : 拦截,不加载
    
    return NO;// 拦截,不执行请求
    
}

image.png

1.2 OC ---> JS

- (IBAction)didClickRightItemClick:(id)sender {
    NSLog(@"响应按钮");
    // OC --> JS
   NSLog(@"%@",[self.webView stringByEvaluatingJavaScriptFromString:@"showAlert('mumu')"]);
    
}

index.html

 <script>
        function showAlert(name){
            alert('我是弹窗'+ name);        
         }
    </script>

image.png

1.2 JS ---> OC

在模拟器中 点击html页面的 点击跳转效应OC方法 按钮 <a href="tzedu://getSum:/helloword/js">点击跳转效应OC方法</a>

就会调用shouldStartLoadWithRequest

// 加载所有请求数据,以及控制是否加载
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
}
po request
<NSMutableURLRequest: 0x600003a80ad0> { URL: tzedu://getSum/helloword/js }

NSURL的三个重要属性

    NSString *urlStr = @"https://www.baidu.com/photos";
    NSURL *url2 = [NSURL URLWithString:urlStr];
    NSLog(@"scheme :%@, host :%@, relativePath :%@",url2.scheme,url2.host,url2.relativePath);

打印结果: scheme :https, host :www.baidu.com, relativePath :/photos

    // http://www.example.com/index.html -> http
    // tzedu://getPlus/helloword/js -> tzedu

2. JavaScriptCoreDemo

2.1 JS ---> OC

Demo地址: github.com/tanghaitao/…

#import <JavaScriptCore/JavaScriptCore.h>
@property (nonatomic, strong) JSContext *jsContext;

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    
    NSString *titlt = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    self.title = titlt;
    
    //JSContext就为其提供着运行环境 H5上下文
    JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
 
    self.jsContext = jsContext;
    // token userID
    [jsContext evaluateScript:@"var arr = ['NSLog','隼龙','叶秋']"];
    //点击了弹框按钮...NSLog,隼龙,叶秋,
    // 监听 js的函数的实现 js -> oc
    jsContext[@"showMessage"] = ^{
        NSArray *arr = [JSContext currentArguments];
        NSLog(@"=====%@",arr); 
    };
}

2.2 OC ---> JS

- (IBAction)didClickRightItemAction:(id)sender {
    NSLog(@"响应");
    // OC 调 JS
    [self.jsContext evaluateScript:@"submit()"];
}

index.html

<script>
        function showAlert(){
            showMessage("点击了弹框按钮..."+arr);
        }

        function submit(){
            alert('submit');
        }
    
        function ocCalljs(dict){
            var name = dict['name'];
            var age  = dict['age'];
            alert(name + age);
            // 传回去
            showDict(dict)
        }
    
    </script>

alert('submit');

更多处理,请参考Demo。

3. WKWebView

Demo地址: github.com/tanghaitao/…

WKWebView主要是注意 跨域Cookie失效的问题,解决方法是在原来的cookie上再添加新的数据,具体代码自己网上搜索

一般是使用WKWebViewConfiguration初始化,可以设置request:

image.png

#import <WebKit/WebKit.h>

@interface WKMessageHandleVC ()<WKUIDelegate,WKScriptMessageHandler>
@property (strong, nonatomic) WKWebView   *webView;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    
    NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
    WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    WKUserContentController *wkUController = [[WKUserContentController alloc] init];
    [wkUController addUserScript:wkUScript];
    //比例,大小适应设备尺寸
    config.userContentController = wkUController;

    
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
    self.webView.navigationDelegate = self;
    self.webView.UIDelegate         = self;
    [self.view addSubview:self.webView];
    
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
    [self.webView loadFileURL:url allowingReadAccessToURL:url];
    
}

设置title:

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    self.title = webView.title;
}

由于js的弹框界面不是很好看,推荐使用oc的弹框:

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];
    
    [self presentViewController:alert animated:YES completion:nil];
}

3.1 OC --> JS

 NSString *jsStr2 = @"var arr = [3, 'haitao', 'abc']; ";
    // oc -> js
    [self.webView evaluateJavaScript:jsStr2 completionHandler:^(id _Nullable result, NSError * _Nullable error) {        NSLog(@"%@----%@",result, error);    }];

3.2 JS --> OC (不拦截)

3.2.1 拦截请求(非常规)

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    // 拦截 -- 超链接  --  自定义
//    if (路由) {
//        
//        NSLog(@"%@",navigationAction.request);
//
//        decisionHandler(WKNavigationActionPolicyCancel);
//
//    }

    decisionHandler(WKNavigationActionPolicyAllow);
}

3.2.2 不拦截请求(常规处理方法)

一般wkwebview处理事件handle,在类WKMessageHandleVC.m中, index.html

<script>
        function loadURL(url) {
            var iFrame;
            iFrame = document.createElement("iframe");
            iFrame.setAttribute("src", url);
            iFrame.setAttribute("style", "display:none;");
            iFrame.setAttribute("height", "0px");
            iFrame.setAttribute("width", "0px");
            iFrame.setAttribute("frameborder", "0");
            document.body.appendChild(iFrame);
            iFrame.parentNode.removeChild(iFrame);
            iFrame = null;
        }
        
        function showAlert(messgae){
            alert('我是一个可爱的弹框 \n'+messgae+'\n'+arr[1]);
            return "token";
        }
        function submit(){
            alert('<<<<提示框>>>>>');
            loadURL("tzedu://jsCallOC?username=Cooci&password=123456");
        }
    
        function messgaeHandle(){
             // JS --> OC
            // ANDROID
            window.webkit.messageHandlers.messgaeOC.postMessage("haitao 消息");
        }
    
    
    </script>

JS文件中 JS --> OC的关键代码:

  function messgaeHandle(){
             // JS --> OC
            // ANDROID
            window.webkit.messageHandlers.messgaeOC.postMessage("haitao 消息");
        }

window.webkit.messageHandlers.messgaeOC.postMessage webkit内核处理类messageHandlers, messgaeOC对应viewWillAppear中的name:@"messgaeOC"

  • 点击html页面中的按钮 message,报错

      -[WKMessageHandleVC userContentController:didReceiveScriptMessage:]: unrecognized selector sent to instance 0x7fe02740aa60
    

需要在oc中执行代理函数[WKMessageHandleVC userContentController:didReceiveScriptMessage:]

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
 
    NSLog(@"%@---%@",message.name,message.body); //messgaeOC---haitao 消息
    // 展示不出来 -- 会打开 : iframe
//    if (message.name) {
//        <#statements#>
//    }
    
}
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"messgaeOC"];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"messgaeOC"];
}

上面的需要成对出现,如果不调用disappear会导致循环引用。

4. WebViewJavascriptBridge

Demo地址: github.com/tanghaitao/…

  • 从uiwebview直接过渡到wkwebview,处理逻辑(拦截请求,非拦截代理函数)不需要重新修改
  • pod 'WebViewJavascriptBridge', '~> 6.0.3'
+ (instancetype)bridge:(id)webView {
#if defined supportsWKWebView
    if ([webView isKindOfClass:[WKWebView class]]) {
        return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView];
    }
#endif
        //#define WVJB_WEBVIEW_TYPE UIWebView
    if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]]) {
        WebViewJavascriptBridge* bridge = [[self alloc] init];
        [bridge _platformSpecificSetup:webView];
        return bridge;
    }
    [NSException raise:@"BadWebViewType" format:@"Unknown web view type."];
    return nil;
}

4.1 JS-->OC

#import "ViewController.h"
#import <WebViewJavascriptBridge.h>
#import <WebKit/WebKit.h>

// An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews, UIWebViews & WebViews.
@interface ViewController ()<WKUIDelegate,WKNavigationDelegate>
@property (strong, nonatomic) WKWebView   *webView;
@property (nonatomic, strong) WebViewJavascriptBridge *wjb;
@end

// JS-->OC
    self.wjb = [WebViewJavascriptBridge  bridgeForWebView:self.webView];
    [self.wjb setWebViewDelegate:self];
    [self.wjb  registerHandler:@"jsCallsOC" handler:^(id data, WVJBResponseCallback responseCallback) {
       
        NSLog(@"%@---%@----%@",[NSThread currentThread],data,responseCallback);
    }];

index.html文件:

 <script>
       
       function setupWebViewJavascriptBridge(callback) {
           if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
           if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
           window.WVJBCallbacks = [callback];
           var WVJBIframe = document.createElement('iframe');
           WVJBIframe.style.display = 'none';
           WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
           document.documentElement.appendChild(WVJBIframe);
           setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
       }
    
        setupWebViewJavascriptBridge(function(bridge) {
         // JS 被调用的方法  OCCallJSFunction 定义的标识
            bridge.registerHandler('OCCallJSFunction', function(data, responseCallback) {
                alert('JS方法被调用:'+data);                  
                responseCallback('js执行过了');
            })
         })
                                 
                                 
         function showWBJ(){
             WebViewJavascriptBridge.callHandler('jsCallsOC', {'haitao': '18'}, function(response) {
                  alert(response);
              })
         }
       
    </script>

点击按钮'提交', WebViewJavascriptBridge.callHandler('jsCallsOC', {'haitao': '18'},funx x x,就会响应oc中注册的jsCallsOC处理请求

4.2 OC --> JS

 [self.wjb callHandler:@"OCCallJSFunction" data:@"糊涂蛋" responseCallback:^(id responseData) {
       
        NSLog(@"%@--%@",[NSThread currentThread],responseData);
    }];

更多处理,比如WKNavigationDelegate,请参考Demo

5. WebViewJavascriptBridgeUIWebView

自行学习,具体用法参照Demo

6. CordovaWebView

自行学习,具体用法参照Demo