RQWebViewViewController.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. //
  2. // RQWebViewViewController.m
  3. // RQCommon
  4. //
  5. // Created by 张嵘 on 2018/11/27.
  6. // Copyright © 2018 张嵘. All rights reserved.
  7. //
  8. #import "RQWebViewViewController.h"
  9. #import "WKWebViewJavascriptBridge.h"
  10. #import <AlipaySDK/AlipaySDK.h>
  11. @interface RQWebViewViewController ()
  12. /// webView
  13. @property (nonatomic, weak, readwrite) WKWebView *webView;
  14. /// 进度条
  15. @property (nonatomic, readwrite, strong) UIProgressView *progressView;
  16. /// 返回按钮
  17. @property (nonatomic, readwrite, strong) UIBarButtonItem *backItem;
  18. /// 关闭按钮 (点击关闭按钮 退出WebView)
  19. @property (nonatomic, readwrite, strong) UIBarButtonItem *closeItem;
  20. /// viewModel
  21. @property (nonatomic, strong, readonly) RQWebViewModel *viewModel;
  22. @end
  23. @implementation RQWebViewViewController
  24. @dynamic viewModel;
  25. - (void)dealloc{
  26. RQDealloc;
  27. /// remove observer ,otherwise will crash
  28. [_webView stopLoading];
  29. }
  30. - (void)viewWillAppear:(BOOL)animated {
  31. [super viewWillAppear:animated];
  32. [self.navigationController.navigationBar addSubview:self.progressView];
  33. /// 加载请求数据
  34. [self.webView loadRequest:self.viewModel.request];
  35. }
  36. - (void)viewWillDisappear:(BOOL)animated {
  37. [super viewWillDisappear:animated];
  38. [self.progressView removeFromSuperview];
  39. }
  40. - (void)viewDidLoad {
  41. [super viewDidLoad];
  42. /// 添加断言,request错误 应用直接crash
  43. NSParameterAssert(self.viewModel.request);
  44. self.navigationItem.leftBarButtonItem = self.backItem;
  45. ///CoderMikeHe FIXED: 切记 lightempty_ios 是前端跟H5商量的结果,请勿修改。
  46. NSString *userAgent = @"wechat_ios";
  47. if (!(RQIOSVersion>=9.0)) [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"userAgent":userAgent}];
  48. /// 注册JS
  49. WKUserContentController *userContentController = [[WKUserContentController alloc] init];
  50. /// 这里可以注册JS的处理 涉及公司私有方法 这里笔者不作处理
  51. WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
  52. // CoderMikeHe Fixed : 自适应屏幕宽度js
  53. NSString *jsString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
  54. WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jsString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
  55. // 添加自适应屏幕宽度js调用的方法
  56. [userContentController addUserScript:userScript];
  57. /// 赋值userContentController
  58. configuration.userContentController = userContentController;
  59. WKWebView *webView = [[WKWebView alloc] initWithFrame:RQ_SCREEN_BOUNDS configuration:configuration];
  60. webView.navigationDelegate = self;
  61. webView.UIDelegate = self;
  62. if (@available(iOS 9.0, *)) {
  63. webView.customUserAgent = userAgent;
  64. } else {
  65. // Fallback on earlier versions
  66. }
  67. self.webView = webView;
  68. [self.view addSubview:webView];
  69. /// oc调用js
  70. [webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
  71. NSLog(@"navigator.userAgent.result is ++++ %@", result);
  72. }];
  73. /// 监听数据
  74. @weakify(self);
  75. /// binding self.viewModel.avatarUrlString
  76. [RACObserve(self.webView, title) subscribeNext:^(NSString *titleStr) {
  77. @strongify(self);
  78. NSLog(@"%@",titleStr);
  79. /// CoderMikeHe FIXED: 这里只设置导航栏的title 以免self.title 设置了tabBarItem.title
  80. if (!self.viewModel.shouldDisableWebViewTitle) self.navigationItem.title = self.webView.title;
  81. }];
  82. [RACObserve(self.webView, loading) subscribeNext:^(NSNumber *currentLoadingState) {
  83. BOOL isLoading = [currentLoadingState boolValue];
  84. if (isLoading) {
  85. NSLog(@"--- webView is loading ---");
  86. }else {
  87. NSLog(@"--- webView isn't loading ---");
  88. }
  89. }];
  90. [RACObserve(self.webView, estimatedProgress) subscribeNext:^(NSNumber *estimatedProgressValue) {
  91. NSLog(@"%@",estimatedProgressValue);
  92. @strongify(self);
  93. [self.progressView setAlpha:1.0f];
  94. [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
  95. if(self.webView.estimatedProgress >= 1.0f) {
  96. [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
  97. [self.progressView setAlpha:0.0f];
  98. } completion:^(BOOL finished) {
  99. [self.progressView setProgress:0.0f animated:NO];
  100. }];
  101. }
  102. }];
  103. /// 添加刷新控件
  104. if(self.viewModel.shouldPullDownToRefresh){
  105. [self.webView.scrollView rq_addHeaderRefresh:^(MJRefreshNormalHeader *header) {
  106. @strongify(self);
  107. [self.webView reload];
  108. }];
  109. [self.webView.scrollView.mj_header beginRefreshing];
  110. }
  111. self.webView.scrollView.contentInset = self.contentInset;
  112. /// CoderMikeHe: 适配 iPhone X + iOS 11,去掉安全区域
  113. if (@available(iOS 11.0, *)) {
  114. RQAdjustsScrollViewInsets_Never(webView.scrollView);
  115. }
  116. }
  117. #pragma mark - 事件处理
  118. - (void)_backItemDidClicked{ /// 返回按钮事件处理
  119. /// 可以返回到上一个网页,就返回到上一个网页
  120. if (self.webView.canGoBack) {
  121. [self.webView goBack];
  122. }else{/// 不能返回上一个网页,就返回到上一个界面
  123. /// 判断 是Push还是Present进来的,
  124. if (self.presentingViewController) {
  125. [self.viewModel.services dismissViewModelAnimated:YES completion:NULL];
  126. } else {
  127. [self.viewModel.services popViewModelAnimated:YES];
  128. }
  129. }
  130. }
  131. - (void)_closeItemDidClicked{
  132. /// 判断 是Push还是Present进来的
  133. if (self.presentingViewController) {
  134. [self.viewModel.services dismissViewModelAnimated:YES completion:NULL];
  135. } else {
  136. [self.viewModel.services popViewModelAnimated:YES];
  137. }
  138. }
  139. - (UIEdgeInsets)contentInset{
  140. return UIEdgeInsetsMake(RQ_APPLICATION_TOP_BAR_HEIGHT, 0, 0, 0);
  141. }
  142. #pragma mark - WKScriptMessageHandler
  143. - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
  144. /// js call OC function
  145. }
  146. #pragma mark - WKNavigationDelegate
  147. /// 内容开始返回时调用
  148. - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
  149. /// 不显示关闭按钮
  150. if(self.viewModel.shouldDisableWebViewClose) return;
  151. UIBarButtonItem *backItem = self.navigationItem.leftBarButtonItems.firstObject;
  152. if (backItem) {
  153. if ([self.webView canGoBack]) {
  154. [self.navigationItem setLeftBarButtonItems:@[backItem, self.closeItem]];
  155. } else {
  156. [self.navigationItem setLeftBarButtonItems:@[backItem]];
  157. }
  158. }
  159. }
  160. // 导航完成时,会回调(也就是页面载入完成了)
  161. - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
  162. if (self.viewModel.shouldPullDownToRefresh) [webView.scrollView.mj_header endRefreshing];
  163. }
  164. // 导航失败时会回调
  165. - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
  166. if (self.viewModel.shouldPullDownToRefresh) [webView.scrollView.mj_header endRefreshing];
  167. }
  168. // 在发送请求之前,决定是否跳转
  169. - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
  170. NSLog(@"navigationAction.request.URL: %@", navigationAction.request.URL);
  171. decisionHandler(WKNavigationActionPolicyAllow);
  172. }
  173. - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
  174. decisionHandler(WKNavigationResponsePolicyAllow);
  175. }
  176. - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler {
  177. NSURLCredential *cred = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
  178. completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
  179. }
  180. #pragma mark - WKUIDelegate
  181. - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
  182. /// CoderMike Fixed : 解决点击网页的链接 不跳转的Bug。
  183. WKFrameInfo *frameInfo = navigationAction.targetFrame;
  184. if (![frameInfo isMainFrame]) {
  185. [webView loadRequest:navigationAction.request];
  186. }
  187. return nil;
  188. }
  189. #pragma mark runJavaScript
  190. - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
  191. [NSObject rq_showAlertViewWithTitle:nil message:message confirmTitle:@"我知道了"];
  192. completionHandler();
  193. }
  194. - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
  195. completionHandler(YES);
  196. }
  197. - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
  198. completionHandler(defaultText);
  199. }
  200. #pragma mark - Getter & Setter
  201. - (UIProgressView *)progressView {
  202. if (!_progressView) {
  203. CGFloat progressViewW = RQ_SCREEN_WIDTH;
  204. CGFloat progressViewH = 3;
  205. CGFloat progressViewX = 0;
  206. CGFloat progressViewY = CGRectGetHeight(self.navigationController.navigationBar.frame) - progressViewH + 1;
  207. UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(progressViewX, progressViewY, progressViewW, progressViewH)];
  208. progressView.progressTintColor = RQ_MAIN_TINTCOLOR;
  209. progressView.trackTintColor = [UIColor clearColor];
  210. [self.view addSubview:progressView];
  211. self.progressView = progressView;
  212. }
  213. return _progressView;
  214. }
  215. - (UIBarButtonItem *)backItem
  216. {
  217. if (_backItem == nil) {
  218. _backItem = [UIBarButtonItem rq_backItemWithTitle:@"返回" imageName:@"barbuttonicon_back_15x30" target:self action:@selector(_backItemDidClicked)];
  219. }
  220. return _backItem;
  221. }
  222. - (UIBarButtonItem *)closeItem {
  223. if (!_closeItem) {
  224. _closeItem = [UIBarButtonItem rq_systemItemWithTitle:@"关闭" titleColor:nil imageName:nil target:self selector:@selector(_closeItemDidClicked) textType:YES];
  225. }
  226. return _closeItem;
  227. }
  228. @end