// // RQWebViewViewController.m // RQCommon // // Created by 张嵘 on 2018/11/27. // Copyright © 2018 张嵘. All rights reserved. // #import "RQWebViewViewController.h" #import "WKWebViewJavascriptBridge.h" #import @interface RQWebViewViewController () /// webView @property (nonatomic, weak, readwrite) WKWebView *webView; /// 进度条 @property (nonatomic, readwrite, strong) UIProgressView *progressView; /// 返回按钮 @property (nonatomic, readwrite, strong) UIBarButtonItem *backItem; /// 关闭按钮 (点击关闭按钮 退出WebView) @property (nonatomic, readwrite, strong) UIBarButtonItem *closeItem; /// viewModel @property (nonatomic, strong, readonly) RQWebViewModel *viewModel; @end @implementation RQWebViewViewController @dynamic viewModel; - (void)dealloc{ RQDealloc; /// remove observer ,otherwise will crash [_webView stopLoading]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.navigationController.navigationBar addSubview:self.progressView]; /// 加载请求数据 [self.webView loadRequest:self.viewModel.request]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.progressView removeFromSuperview]; } - (void)viewDidLoad { [super viewDidLoad]; /// 添加断言,request错误 应用直接crash NSParameterAssert(self.viewModel.request); self.navigationItem.leftBarButtonItem = self.backItem; ///CoderMikeHe FIXED: 切记 lightempty_ios 是前端跟H5商量的结果,请勿修改。 NSString *userAgent = @"wechat_ios"; if (!(RQIOSVersion>=9.0)) [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"userAgent":userAgent}]; /// 注册JS WKUserContentController *userContentController = [[WKUserContentController alloc] init]; /// 这里可以注册JS的处理 涉及公司私有方法 这里笔者不作处理 WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; // CoderMikeHe Fixed : 自适应屏幕宽度js NSString *jsString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jsString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; // 添加自适应屏幕宽度js调用的方法 [userContentController addUserScript:userScript]; /// 赋值userContentController configuration.userContentController = userContentController; WKWebView *webView = [[WKWebView alloc] initWithFrame:RQ_SCREEN_BOUNDS configuration:configuration]; webView.navigationDelegate = self; webView.UIDelegate = self; if (@available(iOS 9.0, *)) { webView.customUserAgent = userAgent; } else { // Fallback on earlier versions } self.webView = webView; [self.view addSubview:webView]; /// oc调用js [webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) { NSLog(@"navigator.userAgent.result is ++++ %@", result); }]; /// 监听数据 @weakify(self); /// binding self.viewModel.avatarUrlString [RACObserve(self.webView, title) subscribeNext:^(NSString *titleStr) { @strongify(self); NSLog(@"%@",titleStr); /// CoderMikeHe FIXED: 这里只设置导航栏的title 以免self.title 设置了tabBarItem.title if (!self.viewModel.shouldDisableWebViewTitle) self.navigationItem.title = self.webView.title; }]; [RACObserve(self.webView, loading) subscribeNext:^(NSNumber *currentLoadingState) { BOOL isLoading = [currentLoadingState boolValue]; if (isLoading) { NSLog(@"--- webView is loading ---"); }else { NSLog(@"--- webView isn't loading ---"); } }]; [RACObserve(self.webView, estimatedProgress) subscribeNext:^(NSNumber *estimatedProgressValue) { NSLog(@"%@",estimatedProgressValue); @strongify(self); [self.progressView setAlpha:1.0f]; [self.progressView setProgress:self.webView.estimatedProgress animated:YES]; if(self.webView.estimatedProgress >= 1.0f) { [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{ [self.progressView setAlpha:0.0f]; } completion:^(BOOL finished) { [self.progressView setProgress:0.0f animated:NO]; }]; } }]; /// 添加刷新控件 if(self.viewModel.shouldPullDownToRefresh){ [self.webView.scrollView rq_addHeaderRefresh:^(MJRefreshNormalHeader *header) { @strongify(self); [self.webView reload]; }]; [self.webView.scrollView.mj_header beginRefreshing]; } self.webView.scrollView.contentInset = self.contentInset; /// CoderMikeHe: 适配 iPhone X + iOS 11,去掉安全区域 if (@available(iOS 11.0, *)) { RQAdjustsScrollViewInsets_Never(webView.scrollView); } } #pragma mark - 事件处理 - (void)_backItemDidClicked{ /// 返回按钮事件处理 /// 可以返回到上一个网页,就返回到上一个网页 if (self.webView.canGoBack) { [self.webView goBack]; }else{/// 不能返回上一个网页,就返回到上一个界面 /// 判断 是Push还是Present进来的, if (self.presentingViewController) { [self.viewModel.services dismissViewModelAnimated:YES completion:NULL]; } else { [self.viewModel.services popViewModelAnimated:YES]; } } } - (void)_closeItemDidClicked{ /// 判断 是Push还是Present进来的 if (self.presentingViewController) { [self.viewModel.services dismissViewModelAnimated:YES completion:NULL]; } else { [self.viewModel.services popViewModelAnimated:YES]; } } - (UIEdgeInsets)contentInset{ return UIEdgeInsetsMake(RQ_APPLICATION_TOP_BAR_HEIGHT, 0, 0, 0); } #pragma mark - WKScriptMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ /// js call OC function } #pragma mark - WKNavigationDelegate /// 内容开始返回时调用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { /// 不显示关闭按钮 if(self.viewModel.shouldDisableWebViewClose) return; UIBarButtonItem *backItem = self.navigationItem.leftBarButtonItems.firstObject; if (backItem) { if ([self.webView canGoBack]) { [self.navigationItem setLeftBarButtonItems:@[backItem, self.closeItem]]; } else { [self.navigationItem setLeftBarButtonItems:@[backItem]]; } } } // 导航完成时,会回调(也就是页面载入完成了) - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { if (self.viewModel.shouldPullDownToRefresh) [webView.scrollView.mj_header endRefreshing]; } // 导航失败时会回调 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { if (self.viewModel.shouldPullDownToRefresh) [webView.scrollView.mj_header endRefreshing]; } // 在发送请求之前,决定是否跳转 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSLog(@"navigationAction.request.URL: %@", navigationAction.request.URL); decisionHandler(WKNavigationActionPolicyAllow); } - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { decisionHandler(WKNavigationResponsePolicyAllow); } - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler { NSURLCredential *cred = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, cred); } #pragma mark - WKUIDelegate - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { /// CoderMike Fixed : 解决点击网页的链接 不跳转的Bug。 WKFrameInfo *frameInfo = navigationAction.targetFrame; if (![frameInfo isMainFrame]) { [webView loadRequest:navigationAction.request]; } return nil; } #pragma mark runJavaScript - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { [NSObject rq_showAlertViewWithTitle:nil message:message confirmTitle:@"我知道了"]; completionHandler(); } - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler { completionHandler(YES); } - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler { completionHandler(defaultText); } #pragma mark - Getter & Setter - (UIProgressView *)progressView { if (!_progressView) { CGFloat progressViewW = RQ_SCREEN_WIDTH; CGFloat progressViewH = 3; CGFloat progressViewX = 0; CGFloat progressViewY = CGRectGetHeight(self.navigationController.navigationBar.frame) - progressViewH + 1; UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(progressViewX, progressViewY, progressViewW, progressViewH)]; progressView.progressTintColor = RQ_MAIN_TINTCOLOR; progressView.trackTintColor = [UIColor clearColor]; [self.view addSubview:progressView]; self.progressView = progressView; } return _progressView; } - (UIBarButtonItem *)backItem { if (_backItem == nil) { _backItem = [UIBarButtonItem rq_backItemWithTitle:@"返回" imageName:@"barbuttonicon_back_15x30" target:self action:@selector(_backItemDidClicked)]; } return _backItem; } - (UIBarButtonItem *)closeItem { if (!_closeItem) { _closeItem = [UIBarButtonItem rq_systemItemWithTitle:@"关闭" titleColor:nil imageName:nil target:self selector:@selector(_closeItemDidClicked) textType:YES]; } return _closeItem; } @end