YMWebArticleView.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. //
  2. // YMWebArticleView.m
  3. // MSYOUPAI
  4. //
  5. // Created by YoMi on 2024/2/15.
  6. // Copyright © 2024 MS. All rights reserved.
  7. //
  8. #import "YMWebArticleView.h"
  9. #import "YMWebArticleViewModel.h"
  10. #import "YOUPAILZLiveAudienceVC.h"
  11. #import "YOUPAILCIMSessionVC.h"
  12. @interface YMWebArticleView()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
  13. /// 网页文章VM
  14. @property (nonatomic, strong) YMWebArticleViewModel *viewModel;
  15. /// 浏览器配置
  16. @property (nonatomic, strong) WKWebViewConfiguration *webConfig;
  17. /// 网页文章浏览器
  18. @property (nonatomic, strong) WKWebView *webArticleView;
  19. /// 进度条
  20. @property (nonatomic, strong) UIProgressView *progressView;
  21. /// 当前Url
  22. @property (nonatomic, strong) NSURL *currentUrl;
  23. @end
  24. @implementation YMWebArticleView
  25. - (void)ym_setupViews{
  26. [self addSubview:self.progressView];
  27. [self addSubview:self.webArticleView];
  28. }
  29. - (void)updateConstraints{
  30. [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
  31. make.top.equalTo(self);
  32. make.left.equalTo(self);
  33. make.right.equalTo(self);
  34. make.height.mas_equalTo(adapt(2));
  35. }];
  36. [self.webArticleView mas_makeConstraints:^(MASConstraintMaker *make) {
  37. make.top.equalTo(self.progressView.mas_bottom);
  38. make.left.equalTo(self);
  39. make.right.equalTo(self);
  40. make.bottom.equalTo(self);
  41. }];
  42. [super updateConstraints];
  43. }
  44. - (void)ym_bindViewModel:(YMWebArticleViewModel*)viewModel{
  45. if (!viewModel) {
  46. return;
  47. }
  48. _viewModel = viewModel;
  49. @weakify(self)
  50. [[[[RACObserve(self.viewModel, webArticleUrl) distinctUntilChanged] deliverOnMainThread] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(NSString * webArticleUrl) {
  51. @strongify(self)
  52. [self web_loadURLString:webArticleUrl];
  53. }];
  54. [[self.viewModel.refreshUISubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id result) {
  55. @strongify(self)
  56. [self.webArticleView reload];
  57. }];
  58. self.webArticleView.ba_web_didStartBlock = ^(WKWebView *webView, WKNavigation *navigation) {
  59. @strongify(self)
  60. NSLog(@"开始加载网页");
  61. };
  62. self.webArticleView.ba_web_didFinishBlock = ^(WKWebView *webView, WKNavigation *navigation) {
  63. @strongify(self)
  64. NSLog(@"加载网页结束");
  65. // WKWebview 禁止长按(超链接、图片、文本...)弹出效果
  66. [webView ba_web_stringByEvaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
  67. [webView ba_web_stringByEvaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
  68. [self.webArticleView mas_updateConstraints:^(MASConstraintMaker *make) {
  69. make.height.mas_equalTo([result doubleValue]);
  70. }];
  71. }];
  72. };
  73. self.webArticleView.ba_web_isLoadingBlock = ^(BOOL isLoading, CGFloat progress) {
  74. @strongify(self)
  75. [self web_progressShow];
  76. self.progressView.progress = progress;
  77. if (self.progressView.progress == 1.0f){
  78. [self web_progressHidden];
  79. }
  80. };
  81. self.webArticleView.ba_web_getCurrentUrlBlock = ^(NSURL * _Nonnull currentUrl) {
  82. @strongify(self)
  83. self.currentUrl = currentUrl;
  84. };
  85. }
  86. - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  87. // if ([message.name isEqualToString:@"openUserDetail"]) {
  88. // //int userId,int type // 用户id
  89. // NSLog(@"openUserDetail-方法名:%@", message.name);
  90. // NSLog(@"openUserDetail-参数:%@", message.body);
  91. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  92. // NSDictionary *dict = message.body;
  93. // NSNumber *type = dict[@"type"];
  94. // NSNumber *userId = dict[@"userId"];
  95. // if ([type isEqualToNumber:@1]) {
  96. // [self.viewModel.gotoPersonalPageSubject sendNext:@([userId intValue])];
  97. // }
  98. // }
  99. // }else if ([message.name isEqualToString:@"openUserLive"]){
  100. // // String roomId // 直播间id
  101. // NSLog(@"openUserLive-方法名:%@", message.name);
  102. // NSLog(@"openUserLive-参数:%@", message.body);
  103. // NSNumber *roomId = message.body;
  104. // WS(weakSelf)
  105. // [ZCHUDHelper showWithStatus:nil];
  106. // [LCHttpHelper requestWithURLString:JoinLive parameters:@{@"room_id":[NSString stringWithFormat:@"%@",roomId]} needToken:YES type:HttpRequestTypePost success:^(id responseObject) {
  107. // NSDictionary* dict = (NSDictionary*)responseObject;
  108. // NSInteger code = [[dict objectForKey:@"code"] integerValue];
  109. // if (code == 0) {
  110. // [ZCHUDHelper dismiss];
  111. // [[YOUPAILZChatRoomManager shareManager] youpaifcloseChatRoom];
  112. // YOUPAILZLiveModel *liveModel = [YOUPAILZLiveModel mj_objectWithKeyValues:[dict objectForKey:@"data"]];
  113. // YOUPAILZLiveAudienceVC *audienceVC = [[YOUPAILZLiveAudienceVC alloc] initWithModel:liveModel];
  114. // [[YMGlobalUtils getCurrentVC].navigationController pushViewController:audienceVC animated:YES];
  115. // }else{
  116. // [ZCHUDHelper showTitle:[dict objectForKey:@"message"]];
  117. // }
  118. // } failure:^(NSError *error) {
  119. // [ZCHUDHelper showTitle:error.localizedDescription];
  120. // }];
  121. //
  122. // }else if ([message.name isEqualToString:@"openActivity"]){
  123. // //int linkType,String linkUrl
  124. //
  125. // NSLog(@"openActivity-方法名:%@", message.name);
  126. // NSLog(@"openActivity-参数:%@", message.body);
  127. //
  128. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  129. // NSDictionary *dict = message.body;
  130. // NSNumber *link_type = dict[@"linkType"];
  131. // NSString *link_url = dict[@"linkUrl"];
  132. // if ([link_type isEqualToNumber:@1]) {
  133. // ZCBaseWebVC* baseWeb = [[ZCBaseWebVC alloc]init];
  134. // baseWeb.contentUrl = link_url;
  135. // [[YMGlobalUtils getCurrentVC].navigationController pushViewController:baseWeb animated:YES];
  136. // }else if ([link_type isEqualToNumber:@2]||[link_type isEqualToNumber:@3]){
  137. // [[LCTools getCurrentVC] youpaifpageToStr:link_url];
  138. // }
  139. // }
  140. // }else if ([message.name isEqualToString:@"openMainActivity"]){
  141. // NSLog(@"openMainActivity-方法名:%@", message.name);
  142. // NSLog(@"openMainActivity-参数:%@", message.body);
  143. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  144. // NSDictionary *dict = message.body;
  145. // NSString *type = dict[@"type"];
  146. // [[LCTools getCurrentVC] youpaifpageToStr:type];
  147. // }
  148. // }else if ([message.name isEqualToString:@"showPay"]){
  149. // NSLog(@"showPay-方法名:%@", message.name);
  150. // NSLog(@"showPay-参数:%@", message.body);
  151. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  152. // NSDictionary *dict = message.body;
  153. // NSString *ios_product_id = [NSString stringWithFormat:@"%@",dict[@"ios_product_id"]];
  154. // [self applePay:ios_product_id];
  155. //
  156. // }
  157. // // }else if([message.name isEqualToString:@"showShareDialog"]){
  158. // // if ([message.body isKindOfClass:[NSDictionary class]]) {
  159. // // NSDictionary *dict = message.body;
  160. // // NSString *imageUrl = [NSString stringWithFormat:@"%@",dict[@"imageUrl"]];
  161. // // NSString *weChatUrl = [NSString stringWithFormat:@"%@",dict[@"weChatUrl"]];
  162. // // [self shareWithQrcodeUrl:imageUrl Url:weChatUrl];
  163. // // }
  164. // //
  165. // // }
  166. // }else if([message.name isEqualToString:@"copyInviteCode"]){
  167. // NSLog(@"copyInviteCode-方法名:%@", message.name);
  168. // NSLog(@"copyInviteCode-参数:%@", message.body);
  169. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  170. // NSDictionary *dict = message.body;
  171. // NSString *inviteCode = [NSString stringWithFormat:@"%@",dict[@"inviteCode"]];
  172. // UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
  173. // pasteboard.string = inviteCode;
  174. // [ZCHUDHelper showTitle:@"复制成功,快去粘贴吧!"];
  175. // }
  176. //
  177. // }else if([message.name isEqualToString:@"showPosterDialog"]){
  178. // NSLog(@"showPosterDialog-方法名:%@", message.name);
  179. // NSLog(@"showPosterDialog-参数:%@", message.body);
  180. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  181. // NSDictionary *dict = message.body;
  182. // YOUPAILZShareApplicationModel *model = [YOUPAILZShareApplicationModel mj_objectWithKeyValues:dict];
  183. // YOUPAILZShareApplicationWindow *vc = [[YOUPAILZShareApplicationWindow alloc] init];
  184. // vc.youpaipmodel = model;
  185. // vc.isTouchDismiss = YES;
  186. // [[LCTools getCurrentVC] TFPresentVC:vc completion:^{}];
  187. // }
  188. //
  189. // }else if([message.name isEqualToString:@"getUserId"]){
  190. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  191. // NSDictionary *dict = message.body;
  192. // NSString *methodName = [dict objectForKey:@"fn"];
  193. // [self.wkwebView evaluateJavaScript:[NSString stringWithFormat:@"%@('%@')",methodName,[LCSaveModel getUserModel].youpaipuserinfo.youpaipuser_id] completionHandler:^(id _Nullable result, NSError * _Nullable error) {
  194. // NSLog(@"%@", error);
  195. // }];
  196. // }
  197. //
  198. // }else if([message.name isEqualToString:@"startP2PSession"]){
  199. // NSLog(@"startP2PSession方法名:%@", message.name);
  200. // NSLog(@"startP2PSession参数:%@", message.body);
  201. // NIMSession *session = [NIMSession session:[LCSaveData getServerId] type:NIMSessionTypeP2P];
  202. // if (session) {
  203. // @weakify(self);
  204. // [ZCHUDHelper show];
  205. // [[[NIMSDK sharedSDK] userManager] fetchUserInfos:@[session.sessionId] completion:^(NSArray<NIMUser *> * _Nullable users, NSError * _Nullable error) {
  206. // @strongify(self);
  207. // [ZCHUDHelper dismiss];
  208. // YOUPAILCIMSessionVC *vc = [[YOUPAILCIMSessionVC alloc] initWithSession:session];
  209. // [[LCTools getContainNavigationControllerCurrentVC].navigationController pushViewController:vc animated:YES];
  210. // }];
  211. // }
  212. // }else if ([message.name isEqualToString:@"showH5Dialog"]){
  213. // //int linkType,String linkUrl
  214. // NSLog(@"showH5Dialog方法名:%@", message.name);
  215. // NSLog(@"showH5Dialog参数:%@", message.body);
  216. //
  217. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  218. // NSDictionary *dict = message.body;
  219. // NSString *gravity = dict[@"gravity"];
  220. // CGFloat width = [dict[@"width"] floatValue];
  221. // CGFloat height = [dict[@"height"] floatValue];
  222. // NSString *loadUrl = dict[@"loadUrl"];
  223. //
  224. // YOUPAIHRWebWindow *window = [[YOUPAIHRWebWindow alloc] init];
  225. // window.youpaipgravity = gravity;
  226. // window.youpaipwidth = width;
  227. // window.youpaipheight = height;
  228. // window.youpaipurl = loadUrl;
  229. // [[LCTools getCurrentVC] TFPresentVC:window completion:^{}];
  230. // }
  231. // }else if ([message.name isEqualToString:@"closeWindow"]){
  232. // [[LCTools getContainNavigationControllerCurrentVC].navigationController popViewControllerAnimated:YES];
  233. // }else if([message.name isEqualToString:@"showNewShareDialog"]){
  234. // if ([message.body isKindOfClass:[NSDictionary class]]) {
  235. // NSDictionary *dict = message.body;
  236. // NSString *title = [NSString stringWithFormat:@"%@",dict[@"title"]];
  237. // NSString *linkUrl = [NSString stringWithFormat:@"%@",dict[@"linkUrl"]];
  238. // NSString *thumbUrl = [NSString stringWithFormat:@"%@",dict[@"thumbUrl"]];
  239. // NSString *des = [NSString stringWithFormat:@"%@",dict[@"des"]];
  240. // [self shareWithTitle:title linkUrl:linkUrl thumbUrl:thumbUrl des:des];
  241. // }
  242. // }
  243. //
  244. }
  245. //iOS9.0以上异常终止时调用
  246. - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
  247. [webView reload];
  248. }
  249. - (void)web_progressShow{
  250. // 开始加载网页时展示出progressView
  251. self.progressView.hidden = NO;
  252. // 开始加载网页的时候将progressView的Height恢复为1.5倍
  253. self.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
  254. // 防止progressView被网页挡住
  255. [self bringSubviewToFront:self.progressView];
  256. }
  257. - (void)web_progressHidden{
  258. /*
  259. *添加一个简单的动画,将progressView的Height变为1.4倍,在开始加载网页的代理中会恢复为1.5倍
  260. *动画时长0.25s,延时0.3s后开始动画
  261. *动画结束后将progressView隐藏
  262. */
  263. [UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
  264. self.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.4f);
  265. } completion:^(BOOL finished) {
  266. self.progressView.hidden = YES;
  267. }];
  268. }
  269. /**
  270. * 加载一个 webview
  271. *
  272. * @param request 请求的 NSURL URLRequest
  273. */
  274. - (void)web_loadRequest:(NSURLRequest *)request{
  275. [self.webArticleView ba_web_loadRequest:request];
  276. }
  277. /**
  278. * 加载一个 webview
  279. *
  280. * @param URL 请求的 URL
  281. */
  282. - (void)web_loadURL:(NSURL *)URL{
  283. [self.webArticleView ba_web_loadURL:URL];
  284. }
  285. /**
  286. * 加载一个 webview
  287. *
  288. * @param URLString 请求的 URLString
  289. */
  290. - (void)web_loadURLString:(NSString *)URLString{
  291. [self.webArticleView ba_web_loadURLString:URLString];
  292. }
  293. /**
  294. * 加载本地网页
  295. *
  296. * @param htmlName 请求的本地 HTML 文件名
  297. */
  298. - (void)web_loadHTMLFileName:(NSString *)htmlName{
  299. [self.webArticleView ba_web_loadHTMLFileName:htmlName];
  300. }
  301. /**
  302. * 加载本地 htmlString
  303. *
  304. * @param htmlString 请求的本地 htmlString
  305. */
  306. - (void)web_loadHTMLString:(NSString *)htmlString{
  307. [self.webArticleView ba_web_loadHTMLString:htmlString];
  308. }
  309. /**
  310. * 加载 js 字符串,例如:高度自适应获取代码:
  311. // webView 高度自适应
  312. [self web_stringByEvaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
  313. // 获取页面高度,并重置 webview 的 frame
  314. self.ba_web_currentHeight = [result doubleValue];
  315. CGRect frame = webView.frame;
  316. frame.size.height = self.ba_web_currentHeight;
  317. webView.frame = frame;
  318. }];
  319. *
  320. * @param javaScriptString js 字符串
  321. */
  322. - (void)web_stringByEvaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler{
  323. [self.webArticleView ba_web_stringByEvaluateJavaScript:javaScriptString completionHandler:completionHandler];
  324. }
  325. - (void)setWeb_progressTintColor:(UIColor *)web_progressTintColor{
  326. _web_progressTintColor = web_progressTintColor;
  327. self.progressView.progressTintColor = web_progressTintColor;
  328. }
  329. - (void)setWeb_progressTrackTintColor:(UIColor *)web_progressTrackTintColor{
  330. _web_progressTrackTintColor = web_progressTrackTintColor;
  331. self.progressView.trackTintColor = web_progressTrackTintColor;
  332. }
  333. - (UIProgressView *)progressView {
  334. if (!_progressView){
  335. _progressView = [[UIProgressView alloc] initWithFrame:CGRectZero];
  336. _progressView.tintColor = BAKit_Color_Green_pod;
  337. _progressView.trackTintColor = BAKit_Color_Gray_8_pod;
  338. _progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
  339. }
  340. return _progressView;
  341. }
  342. - (WKWebViewConfiguration *)webConfig{
  343. if (!_webConfig) {
  344. // 创建并配置WKWebView的相关参数
  345. // 1.WKWebViewConfiguration:是WKWebView初始化时的配置类,里面存放着初始化WK的一系列属性;
  346. // 2.WKUserContentController:为JS提供了一个发送消息的通道并且可以向页面注入JS的类,WKUserContentController对象可以添加多个scriptMessageHandler;
  347. // 3.addScriptMessageHandler:name:有两个参数,第一个参数是userContentController的代理对象,第二个参数是JS里发送postMessage的对象。添加一个脚本消息的处理器,同时需要在JS中添加,window.webkit.messageHandlers.<name>.postMessage(<messageBody>)才能起作用。
  348. _webConfig = [[WKWebViewConfiguration alloc] init];
  349. _webConfig.allowsInlineMediaPlayback = YES;
  350. WKUserContentController *userContentController = [[WKUserContentController alloc] init];
  351. ///js调用跳转至用户详情
  352. [userContentController addScriptMessageHandler:self name:@"openUserDetail"];
  353. /// js调用跳转至直播间
  354. [userContentController addScriptMessageHandler:self name:@"openUserLive"];
  355. ///js调用可跳转app指定内页
  356. [userContentController addScriptMessageHandler:self name:@"openActivity"];
  357. /// 支付
  358. [userContentController addScriptMessageHandler:self name:@"showPay"];
  359. // 分享
  360. // [userContentController addScriptMessageHandler:self name:@"showShareDialog"];
  361. /// 复制到剪切板
  362. [userContentController addScriptMessageHandler:self name:@"copyInviteCode"];
  363. /// 分享推广
  364. [userContentController addScriptMessageHandler:self name:@"showPosterDialog"];
  365. /// 把用户id传给H5
  366. [userContentController addScriptMessageHandler:self name:@"getUserId"];
  367. /// 客服
  368. [userContentController addScriptMessageHandler:self name:@"startP2PSession"];
  369. /// js调用Web窗口
  370. [userContentController addScriptMessageHandler:self name:@"showH5Dialog"];
  371. /// 关闭界面
  372. [userContentController addScriptMessageHandler:self name:@"closeWindow"];
  373. /// 第三方分享
  374. [userContentController addScriptMessageHandler:self name:@"showNewShareDialog"];
  375. /// js调用可跳转app指定内页
  376. [userContentController addScriptMessageHandler:self name:@"openMainActivity"];
  377. _webConfig.userContentController = userContentController;
  378. // 初始化偏好设置属性:preferences
  379. _webConfig.preferences = [WKPreferences new];
  380. // The minimum font size in points default is 0;
  381. _webConfig.preferences.minimumFontSize = 15;
  382. // 是否支持 JavaScript
  383. _webConfig.preferences.javaScriptEnabled = YES;
  384. // 不通过用户交互,是否可以打开窗口
  385. _webConfig.preferences.javaScriptCanOpenWindowsAutomatically = NO;
  386. }
  387. return _webConfig;
  388. }
  389. - (WKWebView *)webArticleView{
  390. if (!_webArticleView) {
  391. _webArticleView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:self.webConfig];
  392. [_webArticleView ba_web_initWithDelegate:self.webArticleView.navigationDelegate uIDelegate:self.webArticleView.UIDelegate];
  393. _webArticleView.ba_web_isAutoHeight = NO;
  394. _webArticleView.multipleTouchEnabled = YES;
  395. _webArticleView.autoresizesSubviews = YES;
  396. _webArticleView.opaque = NO;
  397. _webArticleView.backgroundColor = HexColorFromRGB(0xFFFFFF);
  398. _webArticleView.scrollView.showsVerticalScrollIndicator = NO;
  399. }
  400. return _webArticleView;
  401. }
  402. @end