iOS 原生和js交互+本地测试数据
H5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <! 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或者其他参数)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现