iOS 原生和js交互+本地测试数据

H5

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <style>
        *{
           font-size: 50px;
         }
        .btn{height:80px; width:60%;}
   </style>
</head>
<body>
    <div>以下是网页内容---------------</div>
    <div id="js_content"></div>
    <button type="button" onclick="IOSCallJs()">IOS调用js代码</button><br>
    <button type="button" onclick="IOSCallJsWithArgs('js')">IOS调用js代码并传递参数</button><br>
    <button type="button" onclick="jsCallIOS()">JS调用IOS代码</button><br>
    <button type="button" onclick="jsCallIOSWithArgs('ios')">JS调用IOS代码并传递参数</button><br>
    <script>
        //IOS调用js代码无参数
        function IOSCallJs() {
            document.getElementById('js_content').innerHTML = 'hello js';
        }

       //IOS调用js代码带参数
        function IOSCallJsWithArgs(name){
            document.getElementById('js_content').innerHTML = `hello withArgs ${name}`;
        }
        //js调用IOS代码无参数
        function jsCallIOS(){
             window.webkit.messageHandlers.jsInvokeOCMethod.postMessage('Javascript invoke OC');
        }
        //js调用IOS代码有参数
        function jsCallIOSWithArgs(name){
            window.webkit.messageHandlers.jsInvokeOCMethodArgs.postMessage(`Javascript invoke OC Args:${name}`);
        }
    </script>
</body>
</html>

 

ios - oc
 
//
//  FSWebViewController.m
//  Guardian
//
//  Created by djh on 2022/5/6.
//

#import "FSWebViewController.h"
#import <WebKit/WebKit.h>
#import <JavaScriptCore/JavaScriptCore.h>
@interface FSWebViewController ()<WKNavigationDelegate,WKScriptMessageHandler,WKUIDelegate>
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) UIProgressView *progressView;//进度条
@end

@implementation FSWebViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setLeftBarButtonItemWithImage:[UIImage imageNamed:@"导航返回白色"] Action:@selector(goBackAction)];//base 返回按钮 可忽略
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.allowsInlineMediaPlayback = YES;
    config.selectionGranularity = YES;
    config.processPool = [[WKProcessPool alloc]init];
    WKPreferences *preferences = [[WKPreferences alloc] init];
    
    preferences.javaScriptEnabled = YES;
    
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    
    config.preferences = preferences;
    
    NSString *videos = @"document.getElementsByTagName('video');function pauseVideo(){var len = videos.length;for(var i=0;i<len;i++){videos[i].pause();}};var audios = document.getElementsByTagName('audio');function pauseAudio(){var len = audios.length;for(var i=0;i<len;i++){ audios[i].pause();}}";//解决网页语音播放 返回语音未停止播放

    WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:videos injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    
    
    WKUserContentController *userContent = [WKUserContentController new];
    config.userContentController = userContent;
    
    [config.userContentController addUserScript:wkUScript];
    [config.userContentController addScriptMessageHandler:self name:@"jsCallIOSWithArgs"];//js调用iOS方法
    [config.userContentController addScriptMessageHandler:self name:@"close"];
    
    _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0,0, iPhone_Width, iPhone_Height) configuration:config];
    _webView.backgroundColor = [UIColor whiteColor];
    _webView.navigationDelegate = self;
    _webView.UIDelegate = self;
    self.webView.opaque = NO;
    [self.view addSubview:_webView];
    NSLog(@"----------  %@",self.webUrl);
    
    NSString *tempString = [NSString stringWithFormat:@"%@",[[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"]];//本地网页文件 test自己创建文件名
    NSURL * url = [NSURL fileURLWithPath:tempString];
    [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
    
//    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.webUrl]]];

    if ([[UIDevice currentDevice].systemVersion floatValue] >= 11.0) {
        if (@available(iOS 11.0, *)) {
            self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        } else {
        }
    }else{
        self.automaticallyAdjustsScrollViewInsets = false;
    }
    self.progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 2)];
    self.progressView.progressTintColor = [FSFormatTool colorWithHexString:@"#FF6320"];
    [self.view addSubview:self.progressView];
    
    // 给webview添加监听
    [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
    
    
    
}
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self showNavigationBarHidden:NO];
}

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [_webView evaluateJavaScript:@"pauseVideo()"completionHandler:nil];
    [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];

}
-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    self.webView.frame = CGRectMake(0, 0, self.view.bounds.size.width,self.view.mj_h);
}
-(void)goBackAction{
    if (self.webView.canGoBack==YES) {
        [self.webView goBack];
    }else{
        [self popViewController];
    }
}

#pragma mark - WKNavigationDelegate
/* 页面开始加载 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
}
/* 开始返回内容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
    
}
/* 页面加载完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    [self.webView evaluateJavaScript:@"IOSCallJsWithArgs('bb')" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
        NSLog(@"%@",error);
    }];//iOS 调用js有参方法
    
}
/* 页面加载失败 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
}
/* 在发送请求之前,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //允许跳转
    self.title = webView.title;
    //这句是必须加上的,不然会异常
    decisionHandler(WKNavigationActionPolicyAllow);
    
}
/* 在收到响应后,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    
    //这句是必须加上的,不然会异常
    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 接收到服务器跳转请求之后调用

- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    
}

// 数据加载发生错误时调用

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    
    
}

// 需要响应身份验证时调用 同样在block中需要传入用户身份凭证

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void(^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    
    NSURLCredential *newCred = [NSURLCredential credentialWithUser:@"" password:@""  persistence:NSURLCredentialPersistenceNone];
    
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
    
}

// 进程被终止时调用

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    
    NSLog(@"----------进程被终止时调用");
    
}

//依然是这个协议方法,获取注入方法名对象,获取js返回的状态值.

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController
      didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"%@",message.name);//方法名
    NSLog(@"%@",message.body);//传递的数据
    
    if ([message.name isEqualToString:@"jsCallIOSWithArgs"]) {
        NSMutableDictionary *dic = @{}.mutableCopy;
        [dic setValue:@"测试数据123456" forKey:@"jsCallIOSWithArgs"];
        [_webView evaluateJavaScript:[str mj_JSONString] completionHandler:^(id result, NSError *error) {
            KSLog(@"%@",result);
        }];
    }else if ([message.name isEqualToString:@"close"]) {
        [self popViewController];
    }
    
    
}


#pragma mark - WKUIDelegate

- (void)webViewDidClose:(WKWebView *)webView {
    
    NSLog(@"%s", __FUNCTION__);
    
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    NSError *err = nil;
    NSData *dataFromString = [prompt dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:dataFromString options:NSJSONReadingMutableContainers error:&err];
    if (!err)
    {
        NSString *type = [payload objectForKey:@"type"];
        if (type && [type isEqualToString:@"JSbridge"])
        {
            NSString *returnValue = @"";
            NSArray *args = [payload objectForKey:@"arguments"];
            if (args.count < 1) {
                completionHandler(returnValue);
                return;
            }
            NSString * functionName = [args firstObject];
            NSMutableArray * newArrayArgs = [[NSMutableArray alloc]initWithArray:args];
            [newArrayArgs removeObjectAtIndex:0];
            SEL sel = newArrayArgs.count < 1 ? NSSelectorFromString(functionName) : NSSelectorFromString([NSString stringWithFormat:@"%@:",functionName]);
            if ([self respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                returnValue = [self performSelector:sel withObject:newArrayArgs];
#pragma clang diagnostic pop
                if(![returnValue isKindOfClass:[NSString class]])
                {
                    returnValue = @"";
                }
            }
            
            completionHandler(returnValue);
        }
    }
}


#pragma mark - kvo
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"title"]) {
        if (object == self.webView) {
            self.title = self.webView.title;
        }else{
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }else if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
        [self.progressView setAlpha:1.0f];
        [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
        if (self.webView.estimatedProgress  >= 1.0f) {
            [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
                [self.progressView setAlpha:0.0f];
            } completion:^(BOOL finished) {
                [self.progressView setProgress:0.0f animated:YES];
            }];
        }
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
- (void)dealloc{
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
    [self.webView removeObserver:self forKeyPath:@"title"];
    [self.webView setNavigationDelegate:nil];
    [self.webView setUIDelegate:nil];
}
@end

 

 

js端代码

window.webkit.messageHandlers.<双方定义的参数>.postMessage(NULL或者其他参数),参数messageBody里面不能为空什么都不写,不然不会走代理方法 第一次用ios的角度去做js。之前一直为空 前端不反应,后来才找到原因 是不能为空。postMessage()这样的是错误的。

iOS端代码

iOS端调用js端代码的时候里面的IOSCallJsWithArgs('') 里面必须加上单引号 

eg:window.webkit.messageHandlers.close.postMessage(NULL或者其他参数)

posted @ 2022-06-20 13:56  代佳宏  阅读(175)  评论(0编辑  收藏  举报