// // BJWebVC.m // buymore // // Created by yuebin on 2020/12/28. // Copyright © 2020 JLC. All rights reserved. // #import "BJWebVC.h" #import //#import //#import "WKWebViewJavascriptBridge.h" //#import //#import @interface BJWebVC () @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 *)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