502 lines
18 KiB
Objective-C
Executable File
502 lines
18 KiB
Objective-C
Executable File
//
|
||
// 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
|