// // LMJWebViewController.m // PLMMPRJK // // Created by NJHu on 2017/4/9. // Copyright © 2017年 GoMePrjk. All rights reserved. // #import "LMJWebViewController.h" @interface LMJWebViewController () /** <#digest#> */ @property (weak, nonatomic) UIProgressView *progressView; /** <#digest#> */ @property (strong, nonatomic) UIButton *backBtn; /** <#digest#> */ @property (strong, nonatomic) UIButton *closeBtn; @end @implementation LMJWebViewController - (void)setGotoURL:(NSString *)gotoURL { // @"`#%^{}\"[]|\\<> " 最后有一位空格 _gotoURL = [gotoURL stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "] invertedSet]]; } #pragma mark - 生命周期 - (void)viewDidLoad { [super viewDidLoad]; self.fd_interactivePopDisabled = YES; TWWeak(self); [self.webView addObserverBlockForKeyPath:TWKeyPath(weakself.webView, estimatedProgress) block:^(id _Nonnull obj, id _Nullable oldVal, id _Nullable newVal) { weakself.progressView.progress = weakself.webView.estimatedProgress; // 加载完成 if (weakself.webView.estimatedProgress >= 1.0f ) { [UIView animateWithDuration:0.25f animations:^{ weakself.progressView.alpha = 0.0f; weakself.progressView.progress = 0.0f; }]; }else{ weakself.progressView.alpha = 1.0f; } }]; [self.webView addObserverBlockForKeyPath:TWKeyPath(self.webView, title) block:^(id _Nonnull obj, id _Nullable oldVal, id _Nullable newVal) { if (!TWIsEmpty(newVal) && [newVal isKindOfClass:[NSString class]] && [self webViewController:self webViewIsNeedAutoTitle:self.webView]) { weakself.title = newVal; } }]; [self.webView.scrollView addObserverBlockForKeyPath:TWKeyPath(self.webView.scrollView, contentSize) block:^(id _Nonnull obj, id _Nullable oldVal, id _Nullable newVal) { [weakself webView:weakself.webView scrollView:weakself.webView.scrollView contentSize:weakself.webView.scrollView.contentSize]; }]; if (self.gotoURL.length > 0) { [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.gotoURL]]]; }else if (!TWIsEmpty(self.contentHTML)) { [self.webView loadHTMLString:self.contentHTML baseURL:nil]; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self.view bringSubviewToFront:self.progressView]; } #pragma mark - LMJNavUIBaseViewControllerDataSource #pragma mark - 设置左上角的一个返回按钮和一个关闭按钮 /** 导航条的左边的 view */ - (UIView *)lmjNavigationBarLeftView:(LMJNavigationBar *)navigationBar { UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 90, 44)]; leftView.backgroundColor = [UIColor yellowColor]; self.backBtn.mj_origin = CGPointZero; self.closeBtn.lmj_x = leftView.lmj_width - self.closeBtn.lmj_width; [leftView addSubview:self.backBtn]; [leftView addSubview:self.closeBtn]; return leftView; } - (void)leftButtonEvent:(UIButton *)sender navigationBar:(LMJNavigationBar *)navigationBar { [self backBtnClick:sender webView:self.webView]; } - (void)left_close_button_event:(UIButton *)sender { [self closeBtnClick:sender webView:self.webView]; } #pragma mark - LMJWebViewControllerDataSource // 默认需要, 是否需要进度条 - (BOOL)webViewController:(LMJWebViewController *)webViewController webViewIsNeedProgressIndicator:(WKWebView *)webView { return YES; } // 默认需要自动改变标题 - (BOOL)webViewController:(LMJWebViewController *)webViewController webViewIsNeedAutoTitle:(WKWebView *)webView { return YES; } #pragma mark - LMJWebViewControllerDelegate // 导航条左边的返回按钮的点击 - (void)backBtnClick:(UIButton *)backBtn webView:(WKWebView *)webView { if (self.webView.canGoBack) { self.closeBtn.hidden = NO; [self.webView goBack]; }else { [self closeBtnClick:self.closeBtn webView:self.webView]; } } // 关闭按钮的点击 - (void)closeBtnClick:(UIButton *)closeBtn webView:(WKWebView *)webView { // 判断两种情况: push 和 present if ((self.navigationController.presentedViewController || self.navigationController.presentingViewController) && self.navigationController.childViewControllers.count == 1) { [self dismissViewControllerAnimated:YES completion:nil]; }else{ [self.navigationController popViewControllerAnimated:YES]; } } // 监听 self.webView.scrollView 的 contentSize 属性改变,从而对底部添加的自定义 View 进行位置调整 - (void)webView:(WKWebView *)webView scrollView:(UIScrollView *)scrollView contentSize:(CGSize)contentSize { NSLog(@"%@\n%@\n%@", webView, scrollView, NSStringFromCGSize(contentSize)); } #pragma mark - webDelegate // 1, 在发送请求之前,决定是否跳转 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSLog(@"decidePolicyForNavigationAction ==== %@", navigationAction); decisionHandler(WKNavigationActionPolicyAllow); } // 2开始加载时调用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { NSLog(@"didStartProvisionalNavigation ==== %@", navigation); } // 4, 在收到响应后,决定是否跳转 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { NSLog(@"decidePolicyForNavigationResponse ==== %@", navigationResponse); decisionHandler(WKNavigationResponsePolicyAllow); } // 5,内容开始返回时调用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { NSLog(@"didCommitNavigation ==== %@", navigation); } // 3, 6, 加载 HTTPS 的链接,需要权限认证时调用 \ 如果 HTTPS 是用的证书在信任列表中这不要此代理方法 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); // if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // if ([challenge previousFailureCount] == 0) { // // NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; // completionHandler(NSURLSessionAuthChallengeUseCredential, credential); // } else { // // completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); // } // } else { // // completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); // } } // 7页面加载完调用 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { NSLog(@"didFinishNavigation ==== %@", navigation); } // 8页面加载失败时调用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { NSLog(@"didFailProvisionalNavigation ==== %@\nerror ==== %@", navigation, error); [MBProgressHUD showError:@"网页加载失败" ToView:self.view]; } //当 WKWebView 总体内存占用过大,页面即将白屏的时候,系统会调用回调函数 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { [webView reload]; NSLog(@"webViewWebContentProcessDidTerminate"); } #pragma mark - 懒加载 - (WKWebView *)webView { if(_webView == nil) { //初始化一个WKWebViewConfiguration对象 WKWebViewConfiguration *config = [WKWebViewConfiguration new]; //初始化偏好设置属性:preferences config.preferences = [WKPreferences new]; //The minimum font size in points default is 0; config.preferences.minimumFontSize = 0; //是否支持JavaScript config.preferences.javaScriptEnabled = YES; //不通过用户交互,是否可以打开窗口 config.preferences.javaScriptCanOpenWindowsAutomatically = YES; // 检测各种特殊的字符串:比如电话、网站 config.dataDetectorTypes = UIDataDetectorTypeAll; // 播放视频 config.allowsInlineMediaPlayback = YES; // 交互对象设置 // TWWeak(self); // config.userContentController = [[WKUserContentController alloc] init]; // [config.userContentController addScriptMessageHandler:weakself name:<#(nonnull NSString *)#>]; WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [self.view addSubview:webView]; _webView = webView; webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; webView.navigationDelegate = self; webView.UIDelegate = self; webView.opaque = NO; webView.backgroundColor = [UIColor clearColor]; //滑动返回看这里 webView.allowsBackForwardNavigationGestures = YES; if ([self.parentViewController isKindOfClass:[UINavigationController class]]) { UIEdgeInsets contentInset = webView.scrollView.contentInset; contentInset.top += self.lmj_navgationBar.lmj_height; webView.scrollView.contentInset = contentInset; if (@available(iOS 11.0, *)){ webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } webView.scrollView.scrollIndicatorInsets = webView.scrollView.contentInset; } } return _webView; } - (UIProgressView *)progressView { if(_progressView == nil && [self.parentViewController isKindOfClass:[UINavigationController class]]) { UIProgressView *progressView = [[UIProgressView alloc] init]; [self.view addSubview:progressView]; _progressView = progressView; progressView.lmj_height = 1; progressView.lmj_width = kScreenWidth; progressView.lmj_y = self.lmj_navgationBar.lmj_height; progressView.tintColor = [UIColor greenColor]; if ([self respondsToSelector:@selector(webViewController:webViewIsNeedProgressIndicator:)]) { if (![self webViewController:self webViewIsNeedProgressIndicator:self.webView]) { progressView.hidden = YES; } } } return _progressView; } - (UIButton *)backBtn { if(_backBtn == nil) { UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setImage:[UIImage imageNamed:@"navigationButtonReturn"] forState:UIControlStateNormal]; [btn setImage:[UIImage imageNamed:@"navigationButtonReturnClick"] forState:UIControlStateHighlighted]; btn.lmj_size = CGSizeMake(34, 44); [btn addTarget:self action:@selector(leftButtonEvent:navigationBar:) forControlEvents:UIControlEventTouchUpInside]; _backBtn = btn; } return _backBtn; } - (UIButton *)closeBtn { if(_closeBtn == nil) { UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setTitle:@"关闭" forState:UIControlStateNormal]; [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; [btn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateHighlighted]; btn.lmj_size = CGSizeMake(44, 44); btn.hidden = YES; [btn addTarget:self action:@selector(left_close_button_event:) forControlEvents:UIControlEventTouchUpInside]; _closeBtn = btn; } return _closeBtn; } - (void)dealloc { NSLog(@"LMJWebViewController -- dealloc"); [_webView.scrollView removeObserverBlocks]; [_webView removeObserverBlocks]; _webView.UIDelegate = nil; _webView.navigationDelegate = nil; _webView.scrollView.delegate = nil; } @end // UIWebView 使用的权限认证方式, //- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { // if ([navigationAction.request.URL.absoluteString containsString:@"https://"] && IOSVersion < 9.0 && !self.httpsAuth) { // self.originRequest = navigationAction.request; // self.httpsUrlConnection = [[NSURLConnection alloc] initWithRequest:self.originRequest delegate:self]; // [self.httpsUrlConnection start]; // decisionHandler(WKNavigationActionPolicyCancel); // return; // } // decisionHandler(WKNavigationActionPolicyAllow); //} // //- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { // if ([challenge previousFailureCount] == 0) { // self.httpsAuth = YES; // NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; // [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; // } else { // [[challenge sender] cancelAuthenticationChallenge:challenge]; // } //} // //- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // self.httpsAuth = YES; // [self.webView loadRequest:self.originRequest]; // [self.httpsUrlConnection cancel]; //} // //- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { // return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; //}