TUIFaceVerticalView.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. //
  2. // TUIFaceVerticalView.m
  3. // TUIChat
  4. //
  5. // Created by wyl on 2023/11/16.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIFaceVerticalView.h"
  9. @interface TUIFaceVerticalView () <UICollectionViewDelegate,
  10. UICollectionViewDataSource,
  11. UICollectionViewDelegateFlowLayout,
  12. UIPopoverPresentationControllerDelegate>
  13. @property(nonatomic, strong) NSMutableArray *sectionIndexInGroup;
  14. @property(nonatomic, strong) NSMutableArray *groupIndexInSection;
  15. @property(nonatomic, strong) NSMutableDictionary *itemIndexs;
  16. @property(nonatomic, assign) NSInteger sectionCount;
  17. @property(nonatomic, assign) NSInteger curGroupIndex;
  18. @property(nonatomic, strong) UIView *floatCtrlView;
  19. @property(nonatomic, strong) UIButton *sendButton;
  20. @property(nonatomic, strong) UIButton *deleteButton;
  21. //preview
  22. @property (nonatomic, strong) UIImageView *dispalyView;
  23. @property (nonatomic, strong) UIImageView *dispalyImage;
  24. @property (nonatomic, assign) BOOL hasPreViewShow;
  25. @end
  26. @implementation TUIFaceVerticalView
  27. - (id)initWithFrame:(CGRect)frame {
  28. self = [super initWithFrame:frame];
  29. if (self) {
  30. [self setupViews];
  31. [self defaultLayout];
  32. }
  33. return self;
  34. }
  35. - (void)setupViews {
  36. self.backgroundColor = TUIChatDynamicColor(@"chat_input_controller_bg_color", @"#EBF0F6");
  37. _faceFlowLayout = [[TUICollectionRTLFitFlowLayout alloc] init];
  38. _faceFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
  39. _faceFlowLayout.minimumLineSpacing = TFaceView_Margin;
  40. _faceFlowLayout.minimumInteritemSpacing = TFaceView_Margin;
  41. _faceFlowLayout.sectionInset = UIEdgeInsetsMake(0, TFaceView_Page_Padding, 0, TFaceView_Page_Padding);
  42. _faceCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_faceFlowLayout];
  43. [_faceCollectionView registerClass:[TUIFaceCell class] forCellWithReuseIdentifier:TFaceCell_ReuseId];
  44. [_faceCollectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headerView"];
  45. _faceCollectionView.collectionViewLayout = _faceFlowLayout;
  46. _faceCollectionView.pagingEnabled = NO;
  47. _faceCollectionView.delegate = self;
  48. _faceCollectionView.dataSource = self;
  49. _faceCollectionView.showsHorizontalScrollIndicator = NO;
  50. _faceCollectionView.showsVerticalScrollIndicator = NO;
  51. _faceCollectionView.backgroundColor = self.backgroundColor;
  52. _faceCollectionView.alwaysBounceVertical = YES;
  53. [self addSubview:_faceCollectionView];
  54. _lineView = [[UIView alloc] init];
  55. _lineView.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#DBDBDB");
  56. [self addSubview:_lineView];
  57. [self setupfloatCtrlView];
  58. }
  59. - (void)setupfloatCtrlView {
  60. _floatCtrlView = [[UIView alloc] init];
  61. [self addSubview:_floatCtrlView];
  62. _sendButton = [UIButton buttonWithType:UIButtonTypeCustom];
  63. [self.sendButton setTitle:TIMCommonLocalizableString(Send)forState:UIControlStateNormal];
  64. _sendButton.titleLabel.font = [UIFont systemFontOfSize:16];
  65. [self.sendButton addTarget:self action:@selector(didSelectSendButton:) forControlEvents:UIControlEventTouchUpInside];
  66. self.sendButton.backgroundColor = TIMCommonDynamicColor(@"", @"#0069F6");
  67. self.sendButton.layer.cornerRadius = 2;
  68. [_floatCtrlView addSubview:self.sendButton];
  69. _deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  70. [_deleteButton setImage:[UIImage imageWithContentsOfFile:TUIChatFaceImagePath(@"del_normal")] forState:UIControlStateNormal];
  71. [_deleteButton setImageEdgeInsets:UIEdgeInsetsMake(5, 5, 5, 5)];
  72. _deleteButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  73. _deleteButton.layer.cornerRadius = 2;
  74. [_deleteButton addTarget:self action:@selector(didSelectDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
  75. _deleteButton.backgroundColor = [UIColor whiteColor];
  76. [_floatCtrlView addSubview:_deleteButton];
  77. }
  78. - (void)layoutSubviews {
  79. [super layoutSubviews];
  80. [self defaultLayout];
  81. }
  82. - (void)defaultLayout {
  83. _lineView.frame = CGRectMake(0, 0, self.frame.size.width, TLine_Heigh);
  84. [_faceCollectionView mas_remakeConstraints:^(MASConstraintMaker *make) {
  85. make.edges.mas_equalTo(self);
  86. }];
  87. [_floatCtrlView mas_remakeConstraints:^(MASConstraintMaker *make) {
  88. make.trailing.mas_equalTo(self.mas_trailing).mas_offset(-16);
  89. make.bottom.mas_equalTo(self.mas_bottom).mas_offset(20);
  90. make.height.mas_equalTo(88);
  91. make.leading.mas_equalTo(self.deleteButton.mas_leading);
  92. }];
  93. [self.sendButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  94. make.trailing.mas_equalTo(self.floatCtrlView.mas_trailing);
  95. make.top.mas_equalTo(self.floatCtrlView);
  96. make.height.mas_equalTo(30);
  97. make.width.mas_equalTo(50);
  98. }];
  99. [self.deleteButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  100. make.trailing.mas_equalTo(self.sendButton.mas_leading).mas_offset(-10);
  101. make.top.mas_equalTo(self.floatCtrlView);
  102. make.height.mas_equalTo(30);
  103. make.width.mas_equalTo(50);
  104. }];
  105. }
  106. - (void)setData:(NSMutableArray *)data {
  107. _faceGroups = data;
  108. [self defaultLayout];
  109. _sectionIndexInGroup = [NSMutableArray array];
  110. _groupIndexInSection = [NSMutableArray array];
  111. _itemIndexs = [NSMutableDictionary dictionary];
  112. NSInteger sectionIndex = 0;
  113. for (NSInteger groupIndex = 0; groupIndex < _faceGroups.count; ++groupIndex) {
  114. TUIFaceGroup *group = _faceGroups[groupIndex];
  115. [_sectionIndexInGroup addObject:@(sectionIndex)];
  116. int itemCount = group.faces.count;
  117. int sectionCount = ceil(group.faces.count * 1.0 / itemCount);
  118. for (int sectionIndex = 0; sectionIndex < sectionCount; ++sectionIndex) {
  119. [_groupIndexInSection addObject:@(groupIndex)];
  120. }
  121. sectionIndex += sectionCount;
  122. }
  123. _sectionCount = sectionIndex;
  124. for (NSInteger curSection = 0; curSection < _sectionCount; ++curSection) {
  125. NSNumber *groupIndex = _groupIndexInSection[curSection];
  126. NSNumber *groupSectionIndex = _sectionIndexInGroup[groupIndex.integerValue];
  127. TUIFaceGroup *face = _faceGroups[groupIndex.integerValue];
  128. NSInteger itemCount = face.faces.count;
  129. NSInteger groupSection = curSection - groupSectionIndex.integerValue;
  130. for (NSInteger itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
  131. // transpose line/row
  132. NSInteger reIndex = itemIndex;
  133. [_itemIndexs setObject:@(reIndex) forKey:[NSIndexPath indexPathForRow:itemIndex inSection:curSection]];
  134. }
  135. }
  136. _curGroupIndex = 0;
  137. [_faceCollectionView reloadData];
  138. TUIFaceGroup *group = _faceGroups[0];
  139. if (!group.isNeedAddInInputBar) {
  140. _floatCtrlView.hidden = YES;
  141. }
  142. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  143. [self adjustEmotionsAlpha];
  144. });
  145. }
  146. - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
  147. return _sectionCount;
  148. }
  149. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  150. int groupIndex = [_groupIndexInSection[section] intValue];
  151. TUIFaceGroup *group = _faceGroups[groupIndex];
  152. return group.faces.count;
  153. }
  154. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  155. TUIFaceCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:TFaceCell_ReuseId forIndexPath:indexPath];
  156. __weak typeof(self) weakSelf = self;
  157. __weak typeof(cell) weakCell = cell;
  158. cell.longPressCallback = ^(UILongPressGestureRecognizer * _Nonnull recognizer) {
  159. if (weakSelf.hasPreViewShow) {
  160. return;
  161. }
  162. [self showDisplayView:0 display_y:0 targetView:[UIApplication sharedApplication].keyWindow.rootViewController.view faceCell:weakCell];
  163. };
  164. int groupIndex = [_groupIndexInSection[indexPath.section] intValue];
  165. TUIFaceGroup *group = _faceGroups[groupIndex];
  166. int itemCount = group.faces.count;
  167. NSNumber *index = [_itemIndexs objectForKey:indexPath];
  168. if (index.integerValue < group.faces.count) {
  169. TUIFaceCellData *data = group.faces[index.integerValue];
  170. [cell setData:data];
  171. } else {
  172. [cell setData:nil];
  173. }
  174. return cell;
  175. }
  176. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  177. int groupIndex = [_groupIndexInSection[indexPath.section] intValue];
  178. TUIFaceGroup *faces = _faceGroups[groupIndex];
  179. NSNumber *index = [_itemIndexs objectForKey:indexPath];
  180. if (index.integerValue < faces.faces.count) {
  181. if (_delegate && [_delegate respondsToSelector:@selector(faceView:didSelectItemAtIndexPath:)]) {
  182. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index.integerValue inSection:groupIndex];
  183. [_delegate faceView:self didSelectItemAtIndexPath:indexPath];
  184. }
  185. } else {
  186. }
  187. }
  188. - (CGSize)collectionView:(UICollectionView *)collectionView
  189. layout:(UICollectionViewLayout *)collectionViewLayout
  190. sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
  191. int groupIndex = [_groupIndexInSection[indexPath.section] intValue];
  192. TUIFaceGroup *group = _faceGroups[groupIndex];
  193. CGFloat width = (self.frame.size.width - TFaceView_Page_Padding * 2 - TFaceView_Margin * (group.itemCountPerRow - 1)) / group.itemCountPerRow;
  194. CGFloat height = width + TFaceView_Margin;
  195. return CGSizeMake(width, height);
  196. }
  197. - (CGSize)collectionView:(UICollectionView *)collectionView
  198. layout:(UICollectionViewLayout *)collectionViewLayout
  199. referenceSizeForHeaderInSection:(NSInteger)section {
  200. TUIFaceGroup *group = _faceGroups[section];
  201. if (group.groupName.length > 0) {
  202. return CGSizeMake(self.frame.size.width, 20);
  203. }
  204. else {
  205. return CGSizeMake(self.frame.size.width, 0);
  206. }
  207. }
  208. - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
  209. viewForSupplementaryElementOfKind:(NSString *)kind
  210. atIndexPath:(NSIndexPath *)indexPath {
  211. UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
  212. withReuseIdentifier:@"headerView"
  213. forIndexPath:indexPath];
  214. for (UIView *view in headerView.subviews) {
  215. if (view) {
  216. [view removeFromSuperview];
  217. }
  218. }
  219. UILabel *view = [[UILabel alloc]initWithFrame:CGRectMake(TFaceView_Page_Padding, 0, self.frame.size.width, 17)];
  220. view.font = [UIFont systemFontOfSize:12];
  221. view.textColor = TIMCommonDynamicColor(@"", @"#444444");
  222. [headerView addSubview:view];
  223. TUIFaceGroup *group = _faceGroups[indexPath.section];
  224. if (group.groupName.length > 0 ){
  225. view.text = group.groupName;
  226. }
  227. return headerView;
  228. }
  229. - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  230. NSInteger curSection = round(scrollView.contentOffset.x / scrollView.frame.size.width);
  231. if (curSection >= _groupIndexInSection.count) {
  232. return;
  233. }
  234. NSNumber *groupIndex = _groupIndexInSection[curSection];
  235. NSNumber *startSection = _sectionIndexInGroup[groupIndex.integerValue];
  236. if (_curGroupIndex != groupIndex.integerValue) {
  237. _curGroupIndex = groupIndex.integerValue;
  238. if (_delegate && [_delegate respondsToSelector:@selector(faceView:scrollToFaceGroupIndex:)]) {
  239. [_delegate faceView:self scrollToFaceGroupIndex:_curGroupIndex];
  240. }
  241. }
  242. if (scrollView == self.faceCollectionView) {
  243. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  244. [self adjustEmotionsAlpha];
  245. });
  246. }
  247. }
  248. - (void)scrollToFaceGroupIndex:(NSInteger)index {
  249. if (index > _sectionIndexInGroup.count) {
  250. return;
  251. }
  252. NSNumber *start = _sectionIndexInGroup[index];
  253. NSInteger curSection = ceil(_faceCollectionView.contentOffset.x / _faceCollectionView.frame.size.width);
  254. if (curSection > start.integerValue && curSection < start.integerValue ) {
  255. return;
  256. }
  257. CGRect rect =
  258. CGRectMake(start.integerValue * _faceCollectionView.frame.size.width, 0, _faceCollectionView.frame.size.width, _faceCollectionView.frame.size.height);
  259. [_faceCollectionView scrollRectToVisible:rect animated:NO];
  260. [self scrollViewDidScroll:_faceCollectionView];
  261. }
  262. #pragma mark - floatCtrlView
  263. - (void)adjustEmotionsAlpha {
  264. if (self.floatCtrlView.isHidden) {
  265. return;
  266. }
  267. CGRect buttonGruopRect = self.floatCtrlView.frame;
  268. CGRect floatingRect = [self.faceCollectionView convertRect:buttonGruopRect fromView:self];
  269. for (UICollectionViewCell *visibleCell in self.faceCollectionView.visibleCells) {
  270. CGRect cellInCollection = [self.faceCollectionView convertRect:visibleCell.frame toView:self.faceCollectionView];
  271. BOOL ischongdie = CGRectIntersectsRect(floatingRect,cellInCollection);
  272. if(ischongdie){
  273. CGPoint emojiCenterPoint = CGPointMake(CGRectGetMidX(cellInCollection), CGRectGetMidY(cellInCollection));
  274. BOOL containsHalf = CGRectContainsPoint(floatingRect,emojiCenterPoint);
  275. if (containsHalf) {
  276. visibleCell.alpha = 0;
  277. } else {
  278. visibleCell.alpha = 0.5;
  279. }
  280. }
  281. else {
  282. visibleCell.alpha = 1;
  283. }
  284. }
  285. }
  286. - (void)setFloatCtrlViewAllowSendSwitch:(BOOL)isAllow {
  287. if (isAllow) {
  288. self.deleteButton.enabled = YES;
  289. self.sendButton.enabled = YES;
  290. self.deleteButton.alpha = 1;
  291. self.sendButton.alpha = 1;
  292. }
  293. else {
  294. self.deleteButton.enabled = NO;
  295. self.sendButton.enabled = NO;
  296. self.deleteButton.alpha = 0.5;
  297. self.sendButton.alpha = 0.5;
  298. }
  299. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  300. [self adjustEmotionsAlpha];
  301. });
  302. }
  303. - (void)didSelectSendButton:(id)btn {
  304. if (_delegate && [_delegate respondsToSelector:@selector(faceViewClickSendMessageBtn)]) {
  305. [_delegate faceViewClickSendMessageBtn];
  306. }
  307. }
  308. - (void)didSelectDeleteButton:(id)btn {
  309. if (_delegate && [_delegate respondsToSelector:@selector(faceViewDidBackDelete:)]) {
  310. [_delegate faceViewDidBackDelete:(id)self];
  311. }
  312. }
  313. - (void)showDisplayView:(CGFloat)display_x
  314. display_y:(CGFloat)display_y
  315. targetView:(UIView *)targetView
  316. faceCell: (TUIFaceCell *)faceCell{
  317. self.hasPreViewShow = YES;
  318. UIViewController *contentViewController = [[UIViewController alloc] init];
  319. [contentViewController.view addSubview:self.dispalyView];
  320. self.dispalyImage.image = (faceCell.gifImage?:faceCell.face.image);
  321. CGFloat dispalyImagex = 5;
  322. CGFloat dispalyImagey = 5;
  323. CGFloat dispalyImagew = MIN(faceCell.face.image.size.width, 150) ;
  324. CGFloat dispalyImagewh = MIN(faceCell.face.image.size.height, 150);
  325. self.dispalyView.frame = CGRectMake(display_x, display_y, dispalyImagew + 10, dispalyImagewh + 10);
  326. _dispalyImage.frame = CGRectMake(dispalyImagex, dispalyImagey, dispalyImagewh, dispalyImagewh);
  327. contentViewController.view.backgroundColor = [UIColor clearColor
  328. ];
  329. contentViewController.preferredContentSize = CGSizeMake(self.dispalyView.frame.size.width, self.dispalyView.frame.size.height);
  330. contentViewController.modalPresentationStyle = UIModalPresentationPopover;
  331. UIPopoverPresentationController *popoverController = contentViewController.popoverPresentationController;
  332. popoverController.sourceView = self;
  333. popoverController.sourceRect = CGRectMake(0, -10, self.frame.size.width, 0);
  334. popoverController.permittedArrowDirections = UIPopoverArrowDirectionDown;
  335. popoverController.delegate = self;
  336. popoverController.canOverlapSourceViewRect = NO;
  337. [self.mm_viewController presentViewController:contentViewController animated:YES completion:nil];
  338. popoverController.backgroundColor = [UIColor whiteColor];
  339. }
  340. - (UIImageView *)dispalyView {
  341. if (!_dispalyView) {
  342. _dispalyView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)];
  343. _dispalyView.contentMode = UIViewContentModeScaleToFill;
  344. _dispalyView.backgroundColor = [UIColor whiteColor];
  345. [_dispalyView addSubview:self.dispalyImage];
  346. }
  347. return _dispalyView;
  348. }
  349. - (UIImageView *)dispalyImage {
  350. if (!_dispalyImage) {
  351. _dispalyImage = [[UIImageView alloc] init];
  352. }
  353. return _dispalyImage;
  354. }
  355. #pragma mark - UIPopoverPresentationControllerDelegate
  356. - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController {
  357. self.hasPreViewShow = NO;
  358. }
  359. - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
  360. return UIModalPresentationNone;
  361. }
  362. @end