fix: 修复 WKWebView 白屏及 finishActivity JS Bridge 失效问题
- XJBaseWebViewVC: 补全 finishActivity 消息分发,基类默认实现改为 pop 返回 - XJBaseWebViewVC: 新增 decidePolicyForNavigationAction 拦截非 HTTP scheme(alipay:// 等),避免 WKWebView 加载失败导致白屏 - XJBaseWebViewVC: 新增 webViewWebContentProcessDidTerminate,进程被系统杀死后自动 reload - XJBankWebDetailViewController: 移除重复的 finishActivity override,复用基类 pop 逻辑 - AppDelegate: 移除导致启动卡顿的 WKWebView 预热代码 - HomeV2: 将 Controllview 相关文件移入 Controllview 子目录,整理目录结构
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
//前缀请求
|
//前缀请求
|
||||||
//初始化高德地图
|
//初始化高德地图
|
||||||
[self configureAPIKey];
|
[self configureAPIKey];
|
||||||
[self initService];
|
[self initService];
|
||||||
//初始化window
|
//初始化window
|
||||||
[self initWindow];
|
[self initWindow];
|
||||||
|
|||||||
@@ -154,8 +154,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)finishActivity {
|
- (void)finishActivity {
|
||||||
|
// H5 调用 finishActivity 的默认行为:pop 返回上一页
|
||||||
|
// 语义等同于 Android 的 finish(),即关闭当前页面
|
||||||
|
// 子类如需特殊处理(如 dismiss、发通知等),override 此方法即可
|
||||||
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -189,7 +191,7 @@
|
|||||||
WkJSMessageType messageType = [[dic objectForKey:@"messageType"] intValue];
|
WkJSMessageType messageType = [[dic objectForKey:@"messageType"] intValue];
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case WkJSMessageTypeBack:{
|
case WkJSMessageTypeBack:{
|
||||||
// [self.navigationController popViewControllerAnimated:YES];
|
[self.navigationController popViewControllerAnimated:YES];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -199,6 +201,12 @@
|
|||||||
|
|
||||||
} else if ([message.name isEqualToString:@"htmlBack"]) {
|
} else if ([message.name isEqualToString:@"htmlBack"]) {
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"fromWebToReloadData" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"fromWebToReloadData" object:nil];
|
||||||
|
|
||||||
|
} else if ([message.name isEqualToString:@"finishActivity"]) {
|
||||||
|
// JS 调用 window.webkit.messageHandlers.finishActivity.postMessage({}) 时触发
|
||||||
|
// 调用 finishActivity 方法,子类可 override 实现各自的关闭逻辑
|
||||||
|
// 基类默认实现为空,不做任何操作
|
||||||
|
[self finishActivity];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +238,65 @@
|
|||||||
[self hiddenAllMBProgressHUD];
|
[self hiddenAllMBProgressHUD];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - 白屏修复
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截所有导航请求,处理非 HTTP/HTTPS scheme。
|
||||||
|
*
|
||||||
|
* 问题根源:
|
||||||
|
* 银行 H5 点击支付/跳转按钮时,会触发 alipay:// weixin:// tel:// 等 URL Scheme。
|
||||||
|
* WKWebView 默认会尝试自己加载这些 scheme,加载失败后清空页面内容 → 永久白屏。
|
||||||
|
*
|
||||||
|
* 解决方案:
|
||||||
|
* 在此方法中拦截非 HTTP/HTTPS 的请求,交由系统(UIApplication)处理,
|
||||||
|
* WKWebView 自身取消该次导航,页面内容保持不变。
|
||||||
|
*/
|
||||||
|
- (void)webView:(WKWebView *)webView
|
||||||
|
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
||||||
|
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
||||||
|
|
||||||
|
NSURL *url = navigationAction.request.URL;
|
||||||
|
NSString *scheme = url.scheme.lowercaseString;
|
||||||
|
|
||||||
|
// HTTP / HTTPS:正常加载,交给 WKWebView 处理
|
||||||
|
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
|
||||||
|
decisionHandler(WKNavigationActionPolicyAllow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// about:blank:WKWebView 内部使用,直接放行
|
||||||
|
if ([scheme isEqualToString:@"about"]) {
|
||||||
|
decisionHandler(WKNavigationActionPolicyAllow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他 scheme(alipay:// weixin:// tel:// mailto:// 等):
|
||||||
|
// 交由系统打开对应 App 或拨号,WKWebView 取消此次导航,避免白屏
|
||||||
|
if ([[UIApplication sharedApplication] canOpenURL:url]) {
|
||||||
|
[[UIApplication sharedApplication] openURL:url
|
||||||
|
options:@{}
|
||||||
|
completionHandler:nil];
|
||||||
|
}
|
||||||
|
decisionHandler(WKNavigationActionPolicyCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebContent 进程被系统终止时的兜底处理。
|
||||||
|
*
|
||||||
|
* 触发场景:
|
||||||
|
* 设备内存严重不足时,系统会强制 kill 掉 WKWebView 的 WebContent 进程。
|
||||||
|
* 此时 WKWebView 会立即变成空白页,且无法响应任何操作。
|
||||||
|
* 若不处理,用户只能退出页面重新进入。
|
||||||
|
*
|
||||||
|
* 解决方案:
|
||||||
|
* 检测到进程终止后,重新加载当前 URL,让用户无感知恢复。
|
||||||
|
*/
|
||||||
|
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
|
||||||
|
NSLog(@"[XJBaseWebViewVC] WebContent 进程被系统终止,自动 reload");
|
||||||
|
// 重新加载,恢复页面内容
|
||||||
|
[webView reload];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// XJBankWebDetailViewController.h
|
||||||
|
// XinjiangProject
|
||||||
|
//
|
||||||
|
// Created by Apple on 2026/3/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "XJBaseWebViewVC.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface XJBankWebDetailViewController : XJBaseWebViewVC
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// XJBankWebDetailViewController.m
|
||||||
|
// XinjiangProject
|
||||||
|
//
|
||||||
|
// Created by Apple on 2026/3/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "XJBankWebDetailViewController.h"
|
||||||
|
|
||||||
|
@interface XJBankWebDetailViewController ()
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation XJBankWebDetailViewController
|
||||||
|
|
||||||
|
- (void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.isHidenNaviBar = true;
|
||||||
|
[self.wkWebView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.left.right.mas_offset(0);
|
||||||
|
make.bottom.mas_offset(-SafetyAreaHeight);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// finishActivity 行为与基类一致(pop 返回),无需 override
|
||||||
|
// 基类 XJBaseWebViewVC 已实现默认 pop 逻辑
|
||||||
|
|
||||||
|
/*
|
||||||
|
#pragma mark - Navigation
|
||||||
|
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||||
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||||
|
// Get the new view controller using [segue destinationViewController].
|
||||||
|
// Pass the selected object to the new view controller.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@end
|
||||||
+149
-39
@@ -4,7 +4,7 @@
|
|||||||
//
|
//
|
||||||
// Created by Apple on 2026/2/9.
|
// Created by Apple on 2026/2/9.
|
||||||
//
|
//
|
||||||
|
#import "XJBankWebDetailViewController.h"
|
||||||
#import "XJHomePageV2ViewController.h"
|
#import "XJHomePageV2ViewController.h"
|
||||||
#import "QuestionViewController.h"
|
#import "QuestionViewController.h"
|
||||||
#import "HealthyQuestionTable.h"
|
#import "HealthyQuestionTable.h"
|
||||||
@@ -76,6 +76,9 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
|
|
||||||
@property (nonatomic, strong) SDCycleScrollView * AIBannerImage;
|
@property (nonatomic, strong) SDCycleScrollView * AIBannerImage;
|
||||||
|
|
||||||
|
//加密后的字符串
|
||||||
|
@property (nonatomic, copy) NSString * secretString;
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -123,6 +126,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
[self initCompleteBlock];
|
[self initCompleteBlock];
|
||||||
[self configLocationManager];
|
[self configLocationManager];
|
||||||
[self reGeocodeAction];
|
[self reGeocodeAction];
|
||||||
|
|
||||||
//请求手表数据
|
//请求手表数据
|
||||||
// 4. 添加 V2TIMSDKListener 的事件监听器,self 是 id<V2TIMSDKListener> 的实现类,如果您不需要监听 IM SDK 的事件,这个步骤可以忽略。
|
// 4. 添加 V2TIMSDKListener 的事件监听器,self 是 id<V2TIMSDKListener> 的实现类,如果您不需要监听 IM SDK 的事件,这个步骤可以忽略。
|
||||||
[[V2TIMManager sharedInstance] addIMSDKListener:self];
|
[[V2TIMManager sharedInstance] addIMSDKListener:self];
|
||||||
@@ -159,23 +163,22 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
|
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
self.polling = YES; //轮询开始
|
self.polling = YES; //轮询开始
|
||||||
|
|
||||||
//请求token获取数据
|
//请求token获取数据
|
||||||
[self verifyTokenData];
|
[self verifyTokenData];
|
||||||
|
|
||||||
//登录了那就走轮询
|
//登录了那就走轮询
|
||||||
if ([HQCommonUtils isLogin]) {
|
if ([HQCommonUtils isLogin]) {
|
||||||
//请求消息列表
|
//请求消息列表
|
||||||
[self getMessageListData];
|
[self getMessageListData];
|
||||||
[self getUserWatchData];
|
[self getUserWatchData];
|
||||||
|
[self getWebKeySecret];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -343,6 +346,37 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
[searchApi getHomeWatchData:parmDic];
|
[searchApi getHomeWatchData:parmDic];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark: 请求key的加密
|
||||||
|
- (void)getWebKeySecret {
|
||||||
|
|
||||||
|
NSString * scheme = SCHEME;
|
||||||
|
|
||||||
|
NSString * host;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
host = @"xj-api.mcrm.vip";
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
host = @"api-jkglpt.iosp.ydpt.tech";
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSString * token = User_Token;
|
||||||
|
|
||||||
|
NSString * keyStirng = [NSString stringWithFormat:@"scheme=%@&host=%@&tokenF=%@",scheme,host,token];
|
||||||
|
|
||||||
|
NSMutableDictionary * parmDic = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
[parmDic setObject:keyStirng forKey:@"str"];
|
||||||
|
|
||||||
|
XJPNetAPI * searchApi = [[XJPNetAPI alloc] init:self tag:@"SECRET" NeedToken:@""];
|
||||||
|
|
||||||
|
[searchApi getWebNeedEncryptionKey:parmDic];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)Failed:(NSString*)message tag:(NSString*)tag
|
- (void)Failed:(NSString*)message tag:(NSString*)tag
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -411,9 +445,6 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
[self getData];
|
[self getData];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([tag isEqualToString:@"IMAPI"]) {
|
if ([tag isEqualToString:@"IMAPI"]) {
|
||||||
@@ -443,9 +474,6 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
[EasyTextView showErrorText:msg];
|
[EasyTextView showErrorText:msg];
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if ([tag isEqualToString:@"SMALLHELP"]) {
|
if ([tag isEqualToString:@"SMALLHELP"]) {
|
||||||
@@ -644,6 +672,17 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
|
|
||||||
self.topView.watchArray = sortedArray;
|
self.topView.watchArray = sortedArray;
|
||||||
}
|
}
|
||||||
|
if ([tag isEqualToString:@"SECRET"]) {
|
||||||
|
DLog(@"%@",response)
|
||||||
|
NSDictionary * responseDic = response;
|
||||||
|
|
||||||
|
if ([responseDic.allKeys containsObject:@"encryptData"]) {
|
||||||
|
self.secretString = responseDic[@"encryptData"];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//是否需要更新
|
//是否需要更新
|
||||||
@@ -722,26 +761,26 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
|
|
||||||
[self.navigationController pushViewController:foundVC animated:true];
|
[self.navigationController pushViewController:foundVC animated:true];
|
||||||
}
|
}
|
||||||
|
//
|
||||||
- (void)smallQuestionAction {
|
//- (void)smallQuestionAction {
|
||||||
|
//
|
||||||
if ([ESCTUIManager TUIUserStatus] == false) {
|
// if ([ESCTUIManager TUIUserStatus] == false) {
|
||||||
|
//
|
||||||
|
//
|
||||||
[self loginImAccount];
|
// [self loginImAccount];
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
|
// NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
|
||||||
|
//
|
||||||
[self showMBProgressHUDWithString:@"咨询中,请稍候..." autoHidden:NO];
|
// [self showMBProgressHUDWithString:@"咨询中,请稍候..." autoHidden:NO];
|
||||||
XJPNetAPI * searchApi = [[XJPNetAPI alloc] init:self tag:@"SMALLHELP" NeedToken:@""];
|
// XJPNetAPI * searchApi = [[XJPNetAPI alloc] init:self tag:@"SMALLHELP" NeedToken:@""];
|
||||||
|
//
|
||||||
[searchApi postSmallHelpGroup:dic];
|
// [searchApi postSmallHelpGroup:dic];
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
- (void)myQuestionAction {
|
- (void)myQuestionAction {
|
||||||
|
|
||||||
@@ -903,14 +942,63 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
}];
|
}];
|
||||||
//代理
|
//代理
|
||||||
self.homeTable.headDelegate = self;
|
self.homeTable.headDelegate = self;
|
||||||
|
kWeakSelf(self);
|
||||||
|
self.homeTable.tableBlock = ^(HomeQusetionRecordsModel * _Nonnull model) {
|
||||||
|
weakself.recomendModel = model;
|
||||||
|
|
||||||
|
if(![HQCommonUtils isLogin]) {
|
||||||
|
KPostNotification(KNotificationLoginStateChange, @NO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//1图文 2视频
|
||||||
|
if ([model.contentType intValue] == 2) {
|
||||||
|
|
||||||
|
XJDoctorMainViewController * doctorVC = [[XJDoctorMainViewController alloc] init];
|
||||||
|
doctorVC.yuyue_id = model.records_id;
|
||||||
|
doctorVC.popType = 1;
|
||||||
|
[weakself.navigationController pushViewController:doctorVC animated:YES];
|
||||||
|
}
|
||||||
|
if ([model.contentType intValue] == 1) {
|
||||||
|
|
||||||
|
if ([TUILogin isUserLogined]) {
|
||||||
|
ChatViewController * chatVC = [[ChatViewController alloc] init];
|
||||||
|
chatVC.isGroupID = YES;
|
||||||
|
chatVC.chatID = model.imId ;
|
||||||
|
chatVC.eableSendReport = true;
|
||||||
|
chatVC.eablehaveHelathData = false;
|
||||||
|
chatVC.navTitle = @"图文咨询";
|
||||||
|
chatVC.isVideo = NO;
|
||||||
|
chatVC.pictureEnd_id = model.records_id;
|
||||||
|
chatVC.questionType = 2;
|
||||||
|
chatVC.medicalRecordsId = model.memberId;
|
||||||
|
if ([model.contentStatus intValue] == 5) {
|
||||||
|
chatVC.isEveal = true;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
chatVC.isEveal = false;
|
||||||
|
}
|
||||||
|
if ([model.contentStatus intValue] >=4 ) {
|
||||||
|
chatVC.endRecordType = 100;
|
||||||
|
}
|
||||||
|
[weakself.navigationController pushViewController:chatVC animated:YES];
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if ([HQCommonUtils isLogin]) {
|
||||||
|
[weakself loginImAccountList];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//监听
|
//监听
|
||||||
[self.homeTable addObserver:self
|
[self.homeTable addObserver:self
|
||||||
forKeyPath:@"contentSize"
|
forKeyPath:@"contentSize"
|
||||||
options:NSKeyValueObservingOptionNew
|
options:NSKeyValueObservingOptionNew
|
||||||
context:nil];
|
context:nil];
|
||||||
kWeakSelf(self);
|
|
||||||
|
|
||||||
self.homeTable.lookMoreBlock = ^{
|
self.homeTable.lookMoreBlock = ^{
|
||||||
if(![HQCommonUtils isLogin]) {
|
if(![HQCommonUtils isLogin]) {
|
||||||
KPostNotification(KNotificationLoginStateChange, @NO);
|
KPostNotification(KNotificationLoginStateChange, @NO);
|
||||||
@@ -987,6 +1075,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
make.bottom.equalTo(self.contentView.mas_bottom).offset(-18);
|
make.bottom.equalTo(self.contentView.mas_bottom).offset(-18);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1084,10 +1173,31 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半
|
|||||||
}
|
}
|
||||||
if (type == 3) {
|
if (type == 3) {
|
||||||
|
|
||||||
EmptyFunctionDevelopmentVC * vc = [[EmptyFunctionDevelopmentVC alloc] init];
|
// EmptyFunctionDevelopmentVC * vc = [[EmptyFunctionDevelopmentVC alloc] init];
|
||||||
vc.navigationItem.title = @"健康银行";
|
// vc.navigationItem.title = @"健康银行";
|
||||||
[self.navigationController pushViewController:vc animated:YES];
|
// [self.navigationController pushViewController:vc animated:YES];
|
||||||
|
if(![HQCommonUtils isLogin]) {
|
||||||
|
KPostNotification(KNotificationLoginStateChange, @NO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ValidStr(self.secretString)) {
|
||||||
|
[self showMBProgressHUDOnlyWithString:@"网络异常请稍候再试~"];
|
||||||
|
[self getWebKeySecret];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XJBankWebDetailViewController * bankVC = [[XJBankWebDetailViewController alloc] init];
|
||||||
|
|
||||||
|
NSString * urlString;
|
||||||
|
#if DEBUG
|
||||||
|
urlString = @"https://gstest.superwx.cn/healthbank/index.html?";
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
urlString = @"https://console-jkglpt.iosp.ydpt.tech/hb/healthweb/index.html";
|
||||||
|
|
||||||
|
#endif
|
||||||
|
bankVC.webUrl = [NSString stringWithFormat:@"%@?param=%@",urlString,self.secretString];
|
||||||
|
[self.navigationController pushViewController:bankVC animated:YES];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user