YMIMMessageCollectionView.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. //
  2. // YMIMMessageCollectionView.m
  3. // PCDBank
  4. //
  5. // Created by Gusont on 2021/8/25.
  6. // Copyright © 2021 SLAiELTS. All rights reserved.
  7. //
  8. #import "YMIMMessageCollectionView.h"
  9. #import "YMIMBaseMessageCell.h"
  10. #import "UICollectionView+YMHelper.h"
  11. @interface YMIMMessageCollectionView()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate>
  12. @property (nonatomic, assign) BOOL scrollViewDidEnd;
  13. @property (nonatomic, assign) CGFloat scrollViewOfsetY;
  14. @end
  15. @implementation YMIMMessageCollectionView
  16. - (void)dealloc
  17. {
  18. [self saveChatData];
  19. }
  20. - (void)saveChatData {
  21. NSString *key = [NSString stringWithFormat:@"%@--to--%@",[SLGlobalInfo SharedInstance].loginInfo.user.userId, self.friendModel.userId];
  22. NSArray *dictArr = [NSObject mj_keyValuesArrayWithObjectArray:self.chatDataManager];
  23. [[NSUserDefaults standardUserDefaults] setObject:dictArr forKey:key];
  24. [[NSUserDefaults standardUserDefaults] synchronize];
  25. }
  26. - (instancetype)initWithFrame:(CGRect)frame
  27. {
  28. self = [super initWithFrame:frame];
  29. if (self) {
  30. [self setupMessageCollectionView];
  31. }
  32. return self;
  33. }
  34. - (void)setupMessageCollectionView
  35. {
  36. [self.messageCollectionView mas_makeConstraints:^(MASConstraintMaker *make) {
  37. make.edges.equalTo(self);
  38. }];
  39. [self.messageCollectionView registerCellWithReuseIdentifierClass:YMIMBaseMessageCell.class];
  40. }
  41. - (void)applicationEnterBackground:(id)noti
  42. {
  43. // if ([_iFlySpeechSynthesizer isSpeaking]) {
  44. // [_iFlySpeechSynthesizer pauseSpeaking];
  45. // }
  46. }
  47. - (CGFloat)getBotttomCellMaxY
  48. {
  49. if (self.chatDataManager.count > 0) {
  50. NSIndexPath *idxPath = [NSIndexPath indexPathForItem:self.chatDataManager.count - 1 inSection:0];
  51. UICollectionViewCell *cell = [self.messageCollectionView cellForItemAtIndexPath:idxPath];
  52. CGRect rect = [self.messageCollectionView convertRect:cell.frame toView:self.messageCollectionView];
  53. return CGRectGetMaxY(rect);
  54. }
  55. return 0;
  56. }
  57. - (void)scrollToBottom:(BOOL)animated
  58. {
  59. dispatch_async(dispatch_get_main_queue(), ^{
  60. if (self.chatDataManager.count > 0) {
  61. // UIScrollView * scrollView= self.messageCollectionView;
  62. // CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height);
  63. // self.scrollViewDidEnd = NO;
  64. // [scrollView setContentOffset:bottomOffset animated:YES];
  65. NSIndexPath *endIndex = [NSIndexPath indexPathForItem:self.chatDataManager.count - 1 inSection:0];
  66. [self.messageCollectionView scrollToItemAtIndexPath:endIndex atScrollPosition:UICollectionViewScrollPositionBottom animated:animated];
  67. }
  68. });
  69. }
  70. ///Q
  71. - (void)appendMessage:(MessageModel *)message
  72. {
  73. MessageModel *lastModel = self.chatDataManager.lastObject;
  74. if ((lastModel && message.sendTime - lastModel.sendTime > 120) || !lastModel) {
  75. message.showTime = YES;
  76. }
  77. [self appendRequestMessage:message];
  78. [self resultsRequestWith:message];
  79. }
  80. ///A
  81. - (void)appendRequestMessage:(MessageModel *)message
  82. {
  83. [self.chatDataManager addObject:message];
  84. dispatch_async(dispatch_get_main_queue(), ^{
  85. [self.messageCollectionView reloadData];
  86. [self scrollToBottom:YES];
  87. });
  88. }
  89. /// 结果请求数据
  90. /// @param message <#message description#>
  91. - (void)resultsRequestWith:(MessageModel *)message
  92. {
  93. __block MessageModel *msgModel = message;
  94. NSString *msg = message.sendText;
  95. if (msg.length > 0) {
  96. dispatch_async(dispatch_get_main_queue(), ^{
  97. SLMessageSendModel *sendModel = [[SLMessageSendModel alloc] init];
  98. sendModel.chatType = 0;
  99. sendModel.toId = message.toId;
  100. sendModel.message = message.sendText;
  101. [[SLHttpCenter SharedInstance] postWithUrl:@"/api/Chat/SendMessage_WebSocket" body:[sendModel mj_JSONData] success:^(id responseObject) {
  102. if (msgModel.sendFailure) {
  103. msgModel.sendFailure = NO;
  104. }
  105. // NSDictionary *dataDict = [responseObject objectForKey:@"data"];
  106. // NSString *dataStr = [dataDict objectForKey:@"data"];
  107. // NSString *str = [self replaceHasPrefix:@"\n" str:dataStr];
  108. // MessageModel *model = [MessageModel initWithSendText:str isReceive:YES showMike:message.isMike showText:!message.isMike isMike:message.isMike];
  109. // [self appendRequestMessage:model];
  110. } failure:^(SPRequestError *error) {
  111. NSInteger i = [self.chatDataManager indexOfObject:msgModel];
  112. if (i < self.chatDataManager.count && i > -1) {
  113. msgModel.sendFailure = YES;
  114. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  115. [self updateMessage:msgModel idx:i];
  116. });
  117. }
  118. }];
  119. });
  120. }
  121. }
  122. - (NSString *)replaceHasPrefix:(NSString *)fixStr str:(NSString *)str {
  123. if ([str hasPrefix:fixStr]) {
  124. NSString *newStr = [str substringFromIndex:fixStr.length];
  125. return [self replaceHasPrefix:fixStr str:newStr];
  126. }
  127. return str;
  128. }
  129. - (void)setVoiceStatus:(BOOL)voiceStatus
  130. {
  131. _voiceStatus = voiceStatus;
  132. }
  133. #pragma mark - 语音合成
  134. - (void)startSpeakingMessage:(NSString *)responseString
  135. {
  136. }
  137. - (void)insertMessage:(NSArray *)message
  138. {
  139. [self.chatDataManager addObjectsFromArray:message];
  140. [self.messageCollectionView reloadDataNoScroll];
  141. }
  142. - (void)updateMessage:(MessageModel *)message idx:(NSInteger)idx
  143. {
  144. if (self.chatDataManager.count > 1) {
  145. [self.chatDataManager replaceObjectAtIndex:idx withObject:message];
  146. [self.messageCollectionView reloadData];
  147. }
  148. }
  149. - (void)removeMessage:(MessageModel *)message
  150. {
  151. NSInteger idx = [self.chatDataManager indexOfObject:message];
  152. if (message.showTime && self.chatDataManager.count > idx + 1) {
  153. MessageModel *nextModel = [self.chatDataManager objectAtIndex:idx + 1];
  154. nextModel.showTime = YES;
  155. }
  156. [self.chatDataManager removeObject:message];
  157. [self.messageCollectionView reloadDataNoScroll];
  158. }
  159. - (void)removeAllMessages
  160. {
  161. [self.chatDataManager removeAllObjects];
  162. [self.messageCollectionView reloadData];
  163. }
  164. #pragma mark - UICollectionViewDataSource
  165. - (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
  166. {
  167. YMIMBaseMessageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(YMIMBaseMessageCell.class) forIndexPath:indexPath];
  168. MessageModel *rowModel = [self.chatDataManager objectAtIndex:indexPath.item];
  169. [cell confignBaseMessageContentWith:rowModel receiveHeardImg:self.friendModel.userHead];
  170. [cell setCellLayerCornerRadiusWith:rowModel];
  171. WS(weakSelf);
  172. cell.segmentBlock = ^(SLMikeSegmentModel * _Nullable sModel, MessageModel * _Nullable mModel) {
  173. // MessageModel *models = [MessageModel initWithSendText:model.sendText isReceive:model.isReceive showMike:model.showMike showText:model.showText isMike:model.isMike showTime:model.showTime sendTime:model.sendTime voicePath:model.voicePath sendFailure:model.sendFailure sendTextTrans:model.sendTextTrans];
  174. MessageModel *newModel = [MessageModel mj_objectWithKeyValues:[mModel mj_JSONObject]];
  175. switch (sModel.mikeType) {
  176. case SLMikeInputDelete:
  177. {//删除
  178. [weakSelf removeMessage:mModel];
  179. }
  180. break;
  181. case SLMikeInputLook:
  182. {
  183. newModel.showText = YES;
  184. newModel.showTrans = NO;
  185. [weakSelf updateMessage:newModel idx:indexPath.row];
  186. }
  187. break;
  188. case SLMikeInputBroadcast:
  189. {
  190. [weakSelf.chatDataManager enumerateObjectsUsingBlock:^(MessageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  191. if (obj.onBroadcast) {
  192. obj.onBroadcast = NO;
  193. [weakSelf.messageCollectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:idx inSection:0]]];
  194. }
  195. if ([obj isEqual:mModel]) {
  196. obj.onBroadcast = YES;
  197. [weakSelf.messageCollectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:idx inSection:0]]];
  198. }
  199. }];
  200. if (weakSelf.broadcastBlock) {
  201. weakSelf.broadcastBlock(mModel);
  202. }
  203. }
  204. break;
  205. case SLMikeInputHidden:
  206. {
  207. newModel.showText = NO;
  208. newModel.showTrans = NO;
  209. [weakSelf updateMessage:newModel idx:indexPath.row];
  210. }
  211. break;
  212. case SLMikeInputTransHidden:
  213. {
  214. newModel.showTrans = NO;
  215. [weakSelf updateMessage:newModel idx:indexPath.row];
  216. }
  217. break;
  218. case SLMikeInputTrans:
  219. {
  220. newModel.showText = YES;
  221. newModel.showTrans = !newModel.showTrans;
  222. if (!newModel.sendTextTrans) {
  223. //百度智能云
  224. [[SLHttpCenter SharedInstance] getWithUrl:@"https://aip.baidubce.com/oauth/2.0/token" parameter:@{@"grant_type":@"client_credentials", @"client_id":@"0VhDeLqdcbLamGENfw4c24aq", @"client_secret": @"92qphFKyc0uXGktqFLtfdmqlojt9MsgG"} success:^(id responseObject) {
  225. NSString *accessToken = [responseObject objectForKey:@"access_token"];
  226. if (accessToken) {
  227. NSString *bdUrl = [NSString stringWithFormat:@"https://aip.baidubce.com/rpc/2.0/mt/texttrans/v1?access_token=%@",accessToken];
  228. NSDictionary *dict = @{@"q":newModel.sendText,
  229. @"from":@"auto",
  230. @"to":@"zh"};
  231. [[SLHttpCenter SharedInstance] postWithUrl:bdUrl parameter:dict success:^(id responseObject) {
  232. NSDictionary *reslut = [responseObject objectForKey:@"result"];
  233. NSArray *arr = [reslut objectForKey:@"trans_result"];
  234. if (arr && arr.count) {
  235. NSDictionary *resultDict = arr.firstObject;
  236. NSString *dst = [resultDict objectForKey:@"dst"];
  237. newModel.sendTextTrans = dst;
  238. [weakSelf updateMessage:newModel idx:indexPath.row];
  239. }else {
  240. [ZFToast ShowWithMessage:@"翻译失败"];
  241. }
  242. } failure:^(SPRequestError *error) {
  243. [ZFToast ShowWithMessage:@"翻译失败"];
  244. }];
  245. }else {
  246. [ZFToast ShowWithMessage:@"翻译失败"];
  247. }
  248. } failure:^(SPRequestError *error) {
  249. [ZFToast ShowWithMessage:@"翻译失败"];
  250. }];
  251. }else {
  252. [weakSelf updateMessage:newModel idx:indexPath.row];
  253. }
  254. }
  255. break;
  256. case SLMsgRepeat:
  257. {
  258. [weakSelf resultsRequestWith:newModel];
  259. }
  260. break;
  261. case SLMsgCopy:
  262. {
  263. [[UIPasteboard generalPasteboard] setString:newModel.sendText];
  264. }
  265. break;
  266. default:
  267. break;
  268. }
  269. };
  270. return cell;
  271. }
  272. - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
  273. {
  274. [collectionView.collectionViewLayout invalidateLayout];
  275. return 1;
  276. }
  277. - (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  278. {
  279. return self.chatDataManager.count;
  280. }
  281. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  282. {
  283. }
  284. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
  285. {
  286. return UIEdgeInsetsMake(0, 0, 0, 0);
  287. }
  288. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
  289. {
  290. return 0;
  291. }
  292. /**
  293. */
  294. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
  295. {
  296. return 0;
  297. }
  298. - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
  299. {
  300. if (indexPath.item >= self.chatDataManager.count) {
  301. return;
  302. }
  303. }
  304. #pragma mark - UICollectionViewDelegateFlowLayout
  305. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
  306. {
  307. MessageModel *model = [self.chatDataManager objectAtIndex:indexPath.item];
  308. //实际send宽度16 + 52 + 10 + 16 英文和中文宽度不一致、ios string包含字符串iOS开发已经被淘汰了吧
  309. //8 + 48 + 8 + 56
  310. CGFloat sw = 150;
  311. NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
  312. NSRange range = [model.sendText rangeOfCharacterFromSet:set];
  313. if (range.location != NSNotFound) {
  314. sw -= 8;
  315. }
  316. // if (model.sendFailure) {
  317. // sw += 8;
  318. // }
  319. NSString *text = model.sendText;
  320. if (model.showMike && model.showTrans) {
  321. text = model.sendTextTrans;
  322. }
  323. CGSize contentSize = [text boundingRectWithSize:CGSizeMake(kSCREEN_WIDTH - sw, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17]} context:nil].size;
  324. // NSLog(@"%f---%f",contentSize.width, contentSize.height);
  325. CGFloat height = 0;
  326. if (model.showMike) {
  327. height += 65;
  328. }
  329. if (model.showText) {
  330. height += contentSize.height + (model.showMike ? 25 : 42);
  331. }
  332. if (model.showTime) {
  333. height += 23;
  334. }
  335. if (!model.showMike && model.showTrans) {
  336. CGSize transContentSize = [model.sendTextTrans boundingRectWithSize:CGSizeMake(kSCREEN_WIDTH - 124, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17]} context:nil].size;
  337. height += transContentSize.height + 40;
  338. }
  339. return CGSizeMake(kSCREEN_WIDTH, height);
  340. }
  341. //
  342. //- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
  343. //{
  344. // return [[UICollectionReusableView alloc] initWithFrame:CGRectZero];
  345. //}
  346. //
  347. //
  348. //- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
  349. // return CGSizeZero;
  350. //}
  351. - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  352. if (self.scrollViewDidEnd && self.scrollViewOfsetY - scrollView.contentOffset.y > 10) {
  353. self.scrollViewDidEnd = NO;
  354. self.scrollViewOfsetY = 0.0f;
  355. [self.viewController.view endEditing:YES];
  356. }
  357. }
  358. - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
  359. CGFloat ty = self.superview.transform.ty;
  360. if (ty < 0) {
  361. self.scrollViewDidEnd = YES;
  362. self.scrollViewOfsetY = scrollView.contentOffset.y;
  363. }
  364. }
  365. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  366. }
  367. - (UICollectionView *)messageCollectionView
  368. {
  369. if (!_messageCollectionView) {
  370. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  371. layout.minimumLineSpacing = 0.0;
  372. layout.minimumInteritemSpacing = 0.0;
  373. _messageCollectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
  374. _messageCollectionView.delegate = self;
  375. _messageCollectionView.dataSource = self;
  376. _messageCollectionView.backgroundColor = [UIColor clearColor];
  377. _messageCollectionView.showsVerticalScrollIndicator = NO;
  378. _messageCollectionView.showsHorizontalScrollIndicator = NO;
  379. _messageCollectionView.bounces = NO;
  380. [self addSubview:_messageCollectionView];
  381. }
  382. return _messageCollectionView;
  383. }
  384. - (NSMutableArray<MessageModel *> *)chatDataManager
  385. {
  386. if (!_chatDataManager) {
  387. _chatDataManager = [NSMutableArray array];
  388. }
  389. return _chatDataManager;
  390. }
  391. @end