Files
yuyin_ios/SweetParty/主类/旧项目依赖(以后调整)/BJWebVC.m
2025-08-08 11:05:33 +08:00

502 lines
18 KiB
Objective-C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// BJWebVC.m
// buymore
//
// Created by yuebin on 2020/12/28.
// Copyright © 2020 JLC. All rights reserved.
//
#import "BJWebVC.h"
#import <WebKit/WebKit.h>
//#import <JavaScriptCore/JavaScriptCore.h>
//#import "WKWebViewJavascriptBridge.h"
//#import <Contacts/Contacts.h>
//#import <ContactsUI/ContactsUI.h>
@interface BJWebVC () <WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (nonatomic, strong) UIView *progressView;
@property (nonatomic,strong) UIActivityIndicatorView *indicatorView;
@property (nonatomic, assign) BOOL havePushLogin;
@property (nonatomic, assign) BOOL canAddJs;
//@property (strong, nonatomic) WKWebViewJavascriptBridge *webViewBridge;
@property (copy, nonatomic) NSString *fixWebUrl; //拼接通用参数之后网址链接
@property (strong, nonatomic) NSDictionary *jsPassDict; //兼容旧网页使用js传的参数
@end
@implementation BJWebVC
#pragma mark - view
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.hidden = YES;
BlackContentStatusBar
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// self.isUseWebPageTitle = YES;
[self addJsMethod];
[self addObserver];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self removeJsMethod];
[self removeObserve];
}
- (void)viewDidLoad {
[super viewDidLoad];
_canAddJs = YES;
[self initNavBar];
[self initSpecialNavBtn];
[self initWebView];
[self addProgressView];
[self loadRequest];
[self registerNativeFunctions];
// [self performSelector:@selector(callJsAfterUUpay) afterDelay:6];
}
- (void)initNavBar {
self.view.backgroundColor = UIColor.whiteColor;
__weak typeof(self) weakSelf = self;
self.bm_navView = [[CustomNavigationView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, yb_NavigationBar_H)];
[self.bm_navView cus_setNavShow:0];
[self.bm_navView cus_setNavTitle:C_string(_webName)];
UIButton *closeBtn = self.bm_navView.left1Btn;
[closeBtn setTitle:@"" forState:UIControlStateNormal];
[closeBtn setImage:ImageNamed(@"BM_host_nav_btn_close") forState:UIControlStateNormal];
self.bm_navView.handleGuanBiBlock = ^{
[weakSelf closeWeb:nil];
};
self.bm_navView.handleBackBlock = ^{
[weakSelf backWeb:nil];
};
self.bm_navView.hidden = _useH5NavBar>0;
}
// 使用自定义导航栏时顶部的
- (void)initSpecialNavBtn {
if (_useH5NavBar != 0) {
return;
}
__weak typeof(self) weakSelf = self;
if ([_webUrl containsString:@"/help_center"] || [_webUrl containsString:@"index/task/task_bank"]) {
//帮助中心需要右上角的分享按钮
UIButton *shareBtn = self.bm_navView.right0Btn;
shareBtn.hidden = NO;
[shareBtn setTitle:@"" forState:UIControlStateNormal];
[shareBtn setImage:ImageNamed(@"BM_Tools_nav_btn_share") forState:UIControlStateNormal];
self.bm_navView.handleRight0Block = ^{
[weakSelf shareBtnClick];
};
}
}
- (void)initWebView {
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [[WKUserContentController alloc] init];
configuration.preferences = [[WKPreferences alloc] init];
configuration.preferences.minimumFontSize = 10;
configuration.preferences.javaScriptEnabled = YES;
configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
configuration.allowsInlineMediaPlayback = YES; //允许在线播放
configuration.selectionGranularity = YES; //允许可以与网页交互,选择视图
configuration.suppressesIncrementalRendering = YES; //支持记忆读取
// if (GVUSER.isLogin) {
// WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:[NSString stringWithFormat:@"document.cookie=\'%@=%@;domain=%@\';",@"uid", GVUSER.uid,@"http://www.eqidian.net"] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// [configuration.userContentController addUserScript:cookieScript];
//
// WKUserScript *cookieScript1 = [[WKUserScript alloc] initWithSource:[NSString stringWithFormat:@"document.cookie=\'%@=%@;domain=%@\';",@"sign", GVUSER.sign,@"http://www.eqidian.net"] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// [configuration.userContentController addUserScript:cookieScript1];
// }
CGRect rect = CGRectMake(15, yb_NavigationBar_H, APPW-15*2, APPH-yb_NavigationBar_H); //原生导航栏
if (_useH5NavBar == 1) {
rect = CGRectMake(15, yb_StatusBar_H, APPW-15*2, APPH-yb_StatusBar_H);
}else if (_useH5NavBar == 2) {
rect = CGRectMake(15, 0, APPW-15*2, APPH);
}
_webView = [[WKWebView alloc] initWithFrame:rect configuration:configuration];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
[self.view addSubview:_webView];
//禁用苹果的自动适配
if (@available(iOS 11.0, *)) {
self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
_webView.allowsBackForwardNavigationGestures = YES;
_webView.opaque = NO;
}
- (void)addProgressView {
//progress
_progressView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
_progressView.backgroundColor = mainDeepColor;
[_webView addSubview:self.progressView];
//fenghuolun
_indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:self.indicatorView];
self.indicatorView.center = self.view.center;
}
- (void)loadRequest {
if (_webUrl.length == 0) {
return;
}
//链接拼上通用参数
if ([_webUrl containsString:@"?"]) {
_fixWebUrl = [_webUrl stringByAppendingString:@"&iosShow=2&platform=2"];
}else {
_fixWebUrl = [_webUrl stringByAppendingString:@"?iosShow=2&platform=2"];
}
//如果登录就拼上user_id
// if (UserID.length>0 && ![_fixWebUrl containsString:@"user_id"]) {
// _fixWebUrl = [_fixWebUrl stringByAppendingFormat:@"&user_id=%@", UserID];
// }
if ([_fixWebUrl containsString:@"??"]) {
_fixWebUrl = [_fixWebUrl stringByReplacingOccurrencesOfString:@"??" withString:@"?"]; //容错
}
//url编码
_fixWebUrl = [_fixWebUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
//设置请求头和策略:从原始地址确认缓存数据的合法性后,缓存数据就可以使用,否则从原始地址加载
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_fixWebUrl] cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:1];
[request addValue:@"ios" forHTTPHeaderField:@"app-type"];
[request addValue:@"2" forHTTPHeaderField:@"platform"];
if (_httpHeadDict.allKeys.count > 0) {
for (NSString *key in _httpHeadDict.allKeys) {
[request addValue:_httpHeadDict[key] forHTTPHeaderField:key];
}
}
[_webView loadRequest:request];
}
#pragma mark - WKNavigationDelegate
//网页开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self.indicatorView startAnimating];
_progressView.hidden = NO;
_progressView.width = 1;
}
//当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
[self.indicatorView startAnimating];
}
//网页加载完毕
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
_progressView.hidden = YES;
//注入不响应的JS方法即可
// 禁用长按弹出框
[webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:^(id _Nullable result, NSError * _Nullable error) {}];
// 禁用用户选择
[webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none';" completionHandler:^(id _Nullable result, NSError * _Nullable error) {}];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.indicatorView stopAnimating];
if (!_useH5NavBar) {
NSUInteger pageCount = self.webView.backForwardList.backList.count;
self.bm_navView.left1Btn.hidden = pageCount==0;
}
//使用原生导航栏 并且 使用h5标题
if (!_useH5NavBar && (!_useSelfTitle)) {
if (webView.title.length > 0) {
self.bm_navView.titleLab.text = webView.title;
}
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSLog(@"%@====URL",navigationResponse.response.URL);
decisionHandler(WKNavigationResponsePolicyAllow);
}
/**
API授权
*/
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
CFDataRef exceptions = SecTrustCopyExceptions(serverTrust);
SecTrustSetExceptions(serverTrust, exceptions);
CFRelease(exceptions);
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:serverTrust]);
}
//通过导航跳转失败的时候调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.indicatorView stopAnimating];
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.indicatorView stopAnimating];
//关掉特定网址的网络错误框
if ([_webUrl containsString:@"yangkeduo"]) {
NSLog(@"是拼多多网址报错,不显示框");
return;
}
if (_useH5NavBar == 0) {
//原生导航栏
[SVProgressHUD showInfoWithStatus:@"网络错误"];
}else {
//没有原生导航栏时,显示弹框,避免不能返回的尴尬
}
}
#pragma mark - js调用原生有返回值的方法
- (void)registerNativeFunctions {
/*
__weak typeof(self) weakSelf = self;
_webViewBridge = [WKWebViewJavascriptBridge bridgeForWebView:_webView];
[_webViewBridge setWebViewDelegate:self];
[_webViewBridge registerHandler:@"jsGetNativeData" handler:^(id data, WVJBResponseCallback responseCallback) {
NSDictionary *passDict;
if ([data isKindOfClass:NSString.class]) {
passDict = ((NSString *)data).mj_JSONObject;
}
if ([data isKindOfClass:NSDictionary.class]) {
passDict = data;
}
if (passDict) {
BaseJumpModel *model = [[BaseJumpModel alloc] init];
model.md_event_type = [passDict safeStringForKey:@"md_event_type"];
model.md_event_content = [passDict valueForKey:@"md_event_content"];
model.md_event_need_login = [passDict safeBoolForKey:@"md_event_need_login"];
[model doModelEvent];
id result = model.jsNeedThing.mj_JSONString;
if (result) {
NSLog(@"返回给js的值 %@", result);
#if DEBUG
// dispatch_async(dispatch_get_main_queue(), ^{
// [QMUITips showInfo:[NSString stringWithFormat:@"返回给js的值 %@", result]];
// });
#endif
responseCallback(result);
}
}
}];
*/
}
#pragma mark - WKScriptMessageHandler js调用原生方法没有返回值
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"JS 调用了 %@ 方法",message.name);
NSString *jsonString = message.body;
NSString *methodName = message.name;
if ([methodName isEqualToString:@"jsCallNative"]) {
NSDictionary *info;
if ([jsonString isKindOfClass:NSString.class]) {
info = jsonString.mj_JSONObject;
}
if ([jsonString isKindOfClass:NSDictionary.class]) {
info = (NSDictionary *)jsonString; //容错一下万一H5变成字典了
}
if ([info isKindOfClass:NSDictionary.class]) {
// BaseJumpModel *model = [[BaseJumpModel alloc] init];
// model.md_event_type = [info safeStringForKey:@"md_event_type"];
// model.md_event_content = [info valueForKey:@"md_event_content"];
// model.md_event_need_login = [info safeBoolForKey:@"md_event_need_login"];
// [model doModelEvent];
// if (model.md_event_type.integerValue != 1017) {
// NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name,message.body);
// }
}
return;
}
NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name,message.body);
}
#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
NSLog(@"message - %@",message);
completionHandler();
}
//弹出一个输入框与JS交互的
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:prompt message:defaultText preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *a1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
//这里必须执行不然页面会加载不出来
completionHandler(@"");
}];
UIAlertAction *a2 = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"%@", [alert.textFields firstObject].text);
completionHandler([alert.textFields firstObject].text);
}];
[alert addAction:a1];
[alert addAction:a2];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
NSLog(@"%@",textField.text);
}];
[self presentViewController:alert animated:YES completion:nil];
}
//显示一个确认框JS的
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
// [AlertShowTool showAlertWithTitle:message message:nil sureBlock:^{
// completionHandler(YES);
// } cancleBlock:^{
// completionHandler(NO);
// } fromViewController:self];
}
#pragma mark - btn action
- (void)backWeb:(id)sender {
NSInteger pageCount = self.webView.backForwardList.backList.count;
if (pageCount > 0) {
[self.webView goBack];
}else {
[self.navigationController popViewControllerAnimated:YES];
}
}
- (void)closeWeb:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
- (void)shareBtnClick {
}
- (void)reloadWebAfterLogin {
//是否需要确定当前页面在显示中才调用
[self loadRequest];
}
#pragma mark - 原生调用js方法
- (void)callJsAfterUUpay {
NSDictionary *para = @{@"md_event_type":@"1400", @"md_event_content":@{}};
NSString *jsString = [NSString stringWithFormat:@"nativeCallJs('%@')", para.mj_JSONString];
// NSString *jsString = @"nativeCallJs(1400)";
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
- (void)callJsAfterGetPhone:(NSString *)phone withName:(NSString *)name {
NSDictionary *para = @{@"md_event_type":@"1202", @"md_event_content":@{@"phone":C_string(phone), @"name":C_string(name)}};
NSString *jsString = [NSString stringWithFormat:@"nativeCallJs('%@')", para.mj_JSONString];
// NSString *jsString = @"nativeCallJs(1400)";
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
- (void)callJsAfterVideoShow {
NSDictionary *para = @{@"md_event_type":@"1205", @"md_event_content":@{}};
NSString *jsString = [NSString stringWithFormat:@"nativeCallJs('%@')", para.mj_JSONString];
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
#pragma mark - KVO监听
- (void)addJsMethod {
if (_canAddJs) { //连续添加会崩溃
[_webView.configuration.userContentController addScriptMessageHandler:self name:@"jsCallNative"];
_canAddJs = NO;
}
}
- (void)removeJsMethod {
//不然会循环引用,造成内存泄漏
if (_canAddJs == NO) {
[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"jsCallNative"];
_canAddJs = YES;
}
}
- (void)addObserver {
[_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)removeObserve {
@try {
[_webView removeObserver:self forKeyPath:@"estimatedProgress"];
} @catch (NSException *exception) {
NSLog(@"移除观察者出错");
} @finally {
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
[UIView animateWithDuration:0.1 animations:^{
_progressView.width = self.webView.estimatedProgress * APPW;
}];
_progressView.hidden = _webView.estimatedProgress >= 0.97;
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc {
NSLog(@"%@", [NSString stringWithFormat:@"销毁%@页面", NSStringFromClass(self.class)]);
[NSNotificationCenter.defaultCenter removeObserver:self];
}
@end