TUIChatPopMenu.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. //
  2. // TUIChatPopMenu.m
  3. // TUIChat
  4. //
  5. // Created by harvy on 2021/11/30.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIChatPopMenu.h"
  9. #import <TIMCommon/TIMDefine.h>
  10. #import <TUICore/TUIThemeManager.h>
  11. #import "TUIChatPopActionsView.h"
  12. #import <TIMCommon/TIMCommonMediator.h>
  13. #import <TIMCommon/TUIEmojiMeditorProtocol.h>
  14. #import <TUICore/TUICore.h>
  15. #import "TUIFaceView.h"
  16. #define maxColumns 5
  17. #define kContainerInsets UIEdgeInsetsMake(3, 0, 3, 0)
  18. #define kActionWidth 54
  19. #define kActionHeight 65
  20. #define kActionMargin 5
  21. #define kSepartorHeight 0.5
  22. #define kSepartorLRMargin 10
  23. #define kArrowSize CGSizeMake(15, 10)
  24. #define kEmojiHeight 44
  25. @implementation TUIChatPopMenuAction
  26. - (instancetype)initWithTitle:(NSString *)title image:(UIImage *)image weight:(NSInteger)weight callback:(TUIChatPopMenuActionCallback)callback {
  27. if (self = [super init]) {
  28. self.title = title;
  29. self.image = image;
  30. self.weight = weight;
  31. self.callback = callback;
  32. }
  33. return self;
  34. }
  35. @end
  36. @interface TUIChatPopMenu () <UIGestureRecognizerDelegate,V2TIMAdvancedMsgListener>
  37. /**
  38. * emojiRecent view and emoji secondary page view
  39. */
  40. @property(nonatomic, strong) UIView *emojiContainerView;
  41. @property(nonatomic, strong) UIView *containerView;
  42. @property(nonatomic, strong) NSMutableArray *actions;
  43. @property(nonatomic, assign) CGPoint arrawPoint;
  44. @property(nonatomic, assign) CGFloat adjustHeight;
  45. @property(nonatomic, strong) NSMutableDictionary *actionCallback;
  46. @property(nonatomic, strong) CAShapeLayer *arrowLayer;
  47. @property(nonatomic, assign) CGFloat emojiHeight;
  48. @property(nonatomic, strong) TUIChatPopActionsView *actionsView;
  49. @property(nonatomic, assign) BOOL hasEmojiView;
  50. @end
  51. @implementation TUIChatPopMenu
  52. - (void)addAction:(TUIChatPopMenuAction *)action {
  53. if (action) {
  54. [self.actions addObject:action];
  55. }
  56. }
  57. - (void)removeAllAction {
  58. [self.actions removeAllObjects];
  59. }
  60. - (void)setArrawPosition:(CGPoint)point adjustHeight:(CGFloat)adjustHeight {
  61. point = CGPointMake(point.x, point.y - NavBar_Height);
  62. self.arrawPoint = point;
  63. self.adjustHeight = adjustHeight;
  64. }
  65. - (instancetype)initWithEmojiView:(BOOL)hasEmojiView frame:(CGRect)frame {
  66. self.hasEmojiView = hasEmojiView;
  67. return [self initWithFrame:frame];
  68. }
  69. - (instancetype)initWithFrame:(CGRect)frame {
  70. if (self = [super initWithFrame:frame]) {
  71. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
  72. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
  73. tap.delegate = self;
  74. pan.delegate = self;
  75. [self addGestureRecognizer:tap];
  76. [self addGestureRecognizer:pan];
  77. if ([self isAddEmojiView]) {
  78. self.emojiHeight = kEmojiHeight;
  79. }
  80. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(hideWithAnimation) name:@"kTUIChatPopMenuWillHideNotification" object:nil];
  81. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(hideWithAnimation) name:UIKeyboardWillChangeFrameNotification object:nil];
  82. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThemeChanged) name:TUIDidApplyingThemeChangedNotfication object:nil];
  83. [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
  84. }
  85. return self;
  86. }
  87. - (BOOL)isAddEmojiView {
  88. return self.hasEmojiView && [TUIChatConfig defaultConfig].enablePopMenuEmojiReactAction;
  89. }
  90. - (void)onTap:(UIGestureRecognizer *)tap {
  91. [self hideWithAnimation];
  92. }
  93. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  94. if ([touch.view isDescendantOfView:self.emojiContainerView]) {
  95. return NO;
  96. }
  97. if ([touch.view isDescendantOfView:self.containerView]) {
  98. return NO;
  99. }
  100. if (@available(iOS 17.0, *)) {
  101. CGPoint touchPoint = [touch locationInView:touch.view.nextResponder];
  102. CGRect frame = self.targetCell.frame;
  103. if (CGRectContainsPoint(frame, touchPoint)) {
  104. return NO;
  105. }
  106. }
  107. return YES;
  108. }
  109. - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  110. if (@available(iOS 17.0, *)) {
  111. CGPoint touchPoint = [self.superview convertPoint:point fromView:self];
  112. CGRect frame = self.targetCell.frame;
  113. CGRect containerFrame = [self.superview convertRect:self.targetCell.container.frame fromView:self.targetCell];
  114. // CGRect popFrame1 = [self.superview convertRect:self.emojiContainerView.frame fromView:self];
  115. CGRect popFrame2 = [self.superview convertRect:self.containerView.frame fromView:self];
  116. if ( CGRectContainsPoint(popFrame2, touchPoint)) {
  117. return [super hitTest:point withEvent:event];
  118. }
  119. [self.superview convertRect:self.targetCell.container.frame fromView:self.targetCell];
  120. if (CGRectContainsPoint(frame, touchPoint)) {
  121. if ([self.targetCell respondsToSelector:@selector(textView)]) {
  122. UITextView *textView = [self.targetCell valueForKey:@"textView"];
  123. if (CGRectContainsPoint(containerFrame,touchPoint)) {
  124. if (textView && [textView isKindOfClass:UITextView.class] && !textView.isSelectable) {
  125. [textView selectAll:self];
  126. }
  127. return textView;
  128. }else {
  129. if (textView && [textView isKindOfClass:UITextView.class]) {
  130. [textView selectAll:nil];
  131. [self hideWithAnimation];
  132. }
  133. }
  134. } else {
  135. [self hideWithAnimation];
  136. }
  137. return [super hitTest:point withEvent:event];
  138. }
  139. return [super hitTest:point withEvent:event];
  140. }
  141. else {
  142. return [super hitTest:point withEvent:event];
  143. }
  144. }
  145. - (void)hideWithAnimation {
  146. [UIView animateWithDuration:0.3
  147. animations:^{
  148. self.alpha = 0;
  149. }
  150. completion:^(BOOL finished) {
  151. if (finished) {
  152. if (self.hideCallback) {
  153. self.hideCallback();
  154. }
  155. [self removeFromSuperview];
  156. }
  157. }];
  158. }
  159. - (void)hideByClickButton:(UIButton *)button callback:(void (^__nullable)(void))callback {
  160. [UIView animateWithDuration:0.3
  161. animations:^{
  162. self.alpha = 0;
  163. }
  164. completion:^(BOOL finished) {
  165. if (finished) {
  166. if (callback) {
  167. callback();
  168. }
  169. if (self.hideCallback) {
  170. self.hideCallback();
  171. }
  172. [self removeFromSuperview];
  173. }
  174. }];
  175. }
  176. - (void)showInView:(UIView *)window {
  177. if (window == nil) {
  178. window = UIApplication.sharedApplication.keyWindow;
  179. }
  180. self.frame = window.bounds;
  181. [window addSubview:self];
  182. [self layoutSubview];
  183. }
  184. - (void)layoutSubview {
  185. self.layer.shadowColor = [UIColor blackColor].CGColor;
  186. self.layer.shadowRadius = 5;
  187. self.layer.shadowOpacity = 0.5;
  188. [self updateActionByRank];
  189. if ([self isAddEmojiView]) {
  190. [self prepareEmojiView];
  191. }
  192. [self prepareContainerView];
  193. if ([self isAddEmojiView]) {
  194. [self setupEmojiSubView];
  195. }
  196. [self setupContainerPosition];
  197. [self updateLayout];
  198. if (isRTL()) {
  199. [self fitRTLViews];
  200. }
  201. }
  202. - (void)fitRTLViews {
  203. if (self.actionsView) {
  204. for (UIView *subview in self.actionsView.subviews) {
  205. if ([subview respondsToSelector:@selector(resetFrameToFitRTL)]) {
  206. [subview resetFrameToFitRTL];
  207. }
  208. }
  209. }
  210. }
  211. - (void)updateActionByRank {
  212. NSArray *ageSortResultArray = [self.actions sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
  213. TUIChatPopMenuAction *per1 = obj1;
  214. TUIChatPopMenuAction *per2 = obj2;
  215. return per1.weight > per2.weight ? NSOrderedAscending : NSOrderedDescending;
  216. }];
  217. NSMutableArray *filterArray = [NSMutableArray arrayWithArray:ageSortResultArray];
  218. self.actions = [NSMutableArray arrayWithArray:ageSortResultArray];
  219. }
  220. - (void)setupContainerPosition {
  221. /**
  222. * Calculate the coordinates and correct them, the default arrow points down
  223. */
  224. CGFloat minTopBottomMargin = (Is_IPhoneX ? (100) : (0.0));
  225. CGFloat minLeftRightMargin = 50;
  226. CGFloat containerW = self.containerView.bounds.size.width;
  227. CGFloat containerH = self.containerView.bounds.size.height;
  228. CGFloat upContainerY = self.arrawPoint.y + self.adjustHeight + kArrowSize.height; // The containerY value when arrow points up
  229. /**
  230. * The default arrow points down
  231. */
  232. CGFloat containerX = self.arrawPoint.x - 0.5 * containerW;
  233. CGFloat containerY = self.arrawPoint.y - kArrowSize.height - containerH - StatusBar_Height - self.emojiHeight;
  234. BOOL top = NO; // The direction of arrow, here is down
  235. CGFloat arrawX = 0.5 * containerW;
  236. CGFloat arrawY = kArrowSize.height + containerH - 1.5;
  237. /**
  238. * Corrected vertical coordinates
  239. */
  240. if (containerY < minTopBottomMargin) {
  241. /**
  242. * At this time, the container is too high, and it is planned to adjust the direction of the arrow to upward.
  243. */
  244. if (upContainerY + containerH + minTopBottomMargin > self.superview.bounds.size.height) {
  245. /**
  246. * After adjusting the upward arrow direction, it will cause the entire container to exceed the screen. At this time, the adjustment strategy is
  247. * changed to: keep the arrow direction downward and move self.arrawPoint
  248. */
  249. top = NO;
  250. self.arrawPoint = CGPointMake(self.arrawPoint.x, self.arrawPoint.y - containerY);
  251. containerY = self.arrawPoint.y - kArrowSize.height - containerH;
  252. } else {
  253. /**
  254. * Adjust the direction of the arrow to meet the requirements
  255. */
  256. top = YES;
  257. self.arrawPoint = CGPointMake(self.arrawPoint.x, self.arrawPoint.y + self.adjustHeight - StatusBar_Height - 5);
  258. arrawY = -kArrowSize.height;
  259. containerY = self.arrawPoint.y + kArrowSize.height;
  260. }
  261. }
  262. /**
  263. *
  264. * Corrected horizontal coordinates
  265. */
  266. if (containerX < minLeftRightMargin) {
  267. /**
  268. * At this time, the container is too close to the left side of the screen and needs to move to the right
  269. */
  270. CGFloat offset = (minLeftRightMargin - containerX);
  271. arrawX = arrawX - offset;
  272. containerX = containerX + offset;
  273. if (arrawX < 20) {
  274. arrawX = 20;
  275. }
  276. } else if (containerX + containerW + minLeftRightMargin > self.bounds.size.width) {
  277. /**
  278. * At this time, the container is too close to the right side of the screen and needs to be moved to the left
  279. */
  280. CGFloat offset = containerX + containerW + minLeftRightMargin - self.bounds.size.width;
  281. arrawX = arrawX + offset;
  282. containerX = containerX - offset;
  283. if (arrawX > containerW - 20) {
  284. arrawX = containerW - 20;
  285. }
  286. }
  287. self.emojiContainerView.frame = CGRectMake(containerX, containerY, containerW, MAX(self.emojiHeight + containerH, 200));
  288. self.containerView.frame = CGRectMake(containerX, containerY + self.emojiHeight, containerW, containerH);
  289. /**
  290. * Drawing arrow
  291. */
  292. self.arrowLayer = [[CAShapeLayer alloc] init];
  293. self.arrowLayer.path = [self arrawPath:CGPointMake(arrawX, arrawY) directionTop:top].CGPath;
  294. self.arrowLayer.fillColor = TUIChatDynamicColor(@"chat_pop_menu_bg_color", @"#FFFFFF").CGColor;
  295. if (top) {
  296. if (self.emojiContainerView) {
  297. [self.emojiContainerView.layer addSublayer:self.arrowLayer];
  298. } else {
  299. [self.containerView.layer addSublayer:self.arrowLayer];
  300. }
  301. } else {
  302. [self.containerView.layer addSublayer:self.arrowLayer];
  303. }
  304. }
  305. - (void)prepareEmojiView {
  306. if (self.emojiContainerView) {
  307. [self.emojiContainerView removeFromSuperview];
  308. self.emojiContainerView = nil;
  309. }
  310. self.emojiContainerView = [[UIView alloc] init];
  311. [self addSubview:self.emojiContainerView];
  312. }
  313. - (void)prepareContainerView {
  314. if (self.containerView) {
  315. [self.containerView removeFromSuperview];
  316. self.containerView = nil;
  317. }
  318. self.containerView = [[UIView alloc] init];
  319. [self addSubview:self.containerView];
  320. self.actionsView = [[TUIChatPopActionsView alloc] init];
  321. self.actionsView.backgroundColor = TUIChatDynamicColor(@"chat_pop_menu_bg_color", @"#FFFFFF");
  322. [self.containerView addSubview:self.actionsView];
  323. int i = 0;
  324. for (TUIChatPopMenuAction *action in self.actions) {
  325. UIButton *actionButton = [self buttonWithAction:action tag:[self.actions indexOfObject:action]];
  326. [self.actionsView addSubview:actionButton];
  327. i++;
  328. if (i == maxColumns && i < self.actions.count) {
  329. UIView *separtorView = [[UIView alloc] init];
  330. separtorView.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#39393B");
  331. separtorView.hidden = YES;
  332. [self.actionsView addSubview:separtorView];
  333. i = 0;
  334. }
  335. }
  336. /**
  337. * Calculating the size of container
  338. */
  339. int rows = (self.actions.count % maxColumns == 0) ? (int)self.actions.count / maxColumns : (int)(self.actions.count / maxColumns) + 1;
  340. int columns = self.actions.count < maxColumns ? (int)self.actions.count : maxColumns;
  341. if ([self isAddEmojiView]) {
  342. columns = maxColumns;
  343. }
  344. CGFloat width = kActionWidth * columns + kActionMargin * (columns + 1) + kContainerInsets.left + kContainerInsets.right;
  345. CGFloat height = kActionHeight * rows + (rows - 1) * kSepartorHeight + kContainerInsets.top + kContainerInsets.bottom;
  346. self.emojiContainerView.frame = CGRectMake(0, 0, width, self.emojiHeight + height);
  347. self.containerView.frame = CGRectMake(0, self.emojiHeight, width, height);
  348. }
  349. - (void)setupEmojiSubView {
  350. [self setupEmojiRecentView];
  351. [self setupEmojiAdvanceView];
  352. }
  353. - (void)setupEmojiRecentView {
  354. NSDictionary *param = @{TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate : self};
  355. BOOL isRaiseEmojiExtensionSuccess = [TUICore raiseExtension:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_ClassicExtensionID
  356. parentView:self.emojiContainerView
  357. param:param];
  358. if (!isRaiseEmojiExtensionSuccess) {
  359. self.emojiHeight = 0;
  360. }
  361. }
  362. - (void)setupEmojiAdvanceView {
  363. NSDictionary *param = @{TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate : self};
  364. [TUICore raiseExtension:TUICore_TUIChatExtension_ChatPopMenuReactDetailView_ClassicExtensionID parentView:self.emojiContainerView param:param];
  365. }
  366. - (void)updateLayout {
  367. self.actionsView.frame = CGRectMake(0, -0.5, self.containerView.frame.size.width, self.containerView.frame.size.height);
  368. int columns = self.actions.count < maxColumns ? (int)self.actions.count : maxColumns;
  369. CGFloat containerWidth = kActionWidth * columns + kActionMargin * (columns + 1) + kContainerInsets.left + kContainerInsets.right;
  370. int i = 0;
  371. int currentRow = 0;
  372. int currentColumn = 0;
  373. for (UIView *subView in self.actionsView.subviews) {
  374. if ([subView isKindOfClass:UIButton.class]) {
  375. currentRow = i / maxColumns;
  376. currentColumn = i % maxColumns;
  377. CGFloat x = kContainerInsets.left + (currentColumn + 1) * kActionMargin + currentColumn * kActionWidth;
  378. CGFloat y = kContainerInsets.top + currentRow * kActionHeight + currentRow * kSepartorHeight;
  379. subView.frame = CGRectMake(x, y, kActionWidth, kActionHeight);
  380. i++;
  381. } else {
  382. CGFloat y = (currentRow + 1) * kActionHeight + kContainerInsets.top;
  383. CGFloat width = containerWidth - 2 * kSepartorLRMargin - kContainerInsets.left - kContainerInsets.right;
  384. subView.frame = CGRectMake(kSepartorLRMargin, y, width, kSepartorHeight);
  385. }
  386. }
  387. }
  388. - (UIBezierPath *)arrawPath:(CGPoint)point directionTop:(BOOL)top {
  389. CGSize arrowSize = kArrowSize;
  390. UIBezierPath *arrowPath = [[UIBezierPath alloc] init];
  391. [arrowPath moveToPoint:point];
  392. if (top) {
  393. [arrowPath addLineToPoint:CGPointMake(point.x + arrowSize.width * 0.5, point.y + arrowSize.height)];
  394. [arrowPath addLineToPoint:CGPointMake(point.x - arrowSize.width * 0.5, point.y + arrowSize.height)];
  395. } else {
  396. [arrowPath addLineToPoint:CGPointMake(point.x + arrowSize.width * 0.5, point.y - arrowSize.height)];
  397. [arrowPath addLineToPoint:CGPointMake(point.x - arrowSize.width * 0.5, point.y - arrowSize.height)];
  398. }
  399. [arrowPath closePath];
  400. return arrowPath;
  401. }
  402. - (UIButton *)buttonWithAction:(TUIChatPopMenuAction *)action tag:(NSInteger)tag {
  403. UIButton *actionButton = [UIButton buttonWithType:UIButtonTypeCustom];
  404. [actionButton setTitleColor:TUIChatDynamicColor(@"chat_pop_menu_text_color", @"#444444")
  405. forState:UIControlStateNormal];
  406. actionButton.titleLabel.font = [UIFont systemFontOfSize:10.0];
  407. actionButton.titleLabel.numberOfLines = 2;
  408. actionButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
  409. [actionButton setTitle:action.title forState:UIControlStateNormal];
  410. [actionButton setImage:action.image forState:UIControlStateNormal];
  411. actionButton.contentMode = UIViewContentModeScaleAspectFit;
  412. [actionButton addTarget:self action:@selector(buttonHighlightedEnter:) forControlEvents:UIControlEventTouchDown];
  413. [actionButton addTarget:self action:@selector(buttonHighlightedEnter:) forControlEvents:UIControlEventTouchDragEnter];
  414. [actionButton addTarget:self action:@selector(buttonHighlightedExit:) forControlEvents:UIControlEventTouchDragExit];
  415. [actionButton addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
  416. actionButton.tag = tag;
  417. CGSize imageSize = CGSizeMake(20, 20);
  418. CGSize titleSize = actionButton.titleLabel.frame.size;
  419. CGSize textSize = [actionButton.titleLabel.text sizeWithAttributes:@{NSFontAttributeName : actionButton.titleLabel.font}];
  420. CGSize frameSize = CGSizeMake(ceilf(textSize.width), ceilf(textSize.height));
  421. if (titleSize.width + 0.5 < frameSize.width) {
  422. titleSize.width = frameSize.width;
  423. }
  424. titleSize.width = MIN(titleSize.width, 48);
  425. CGFloat totalHeight = (imageSize.height + titleSize.height + 8);
  426. actionButton.imageEdgeInsets = UIEdgeInsetsMake(-(totalHeight - imageSize.height), 0.0, 0.0, -titleSize.width);
  427. actionButton.titleEdgeInsets = UIEdgeInsetsMake(0, -imageSize.width, -(totalHeight - titleSize.height), 0);
  428. [self.actionCallback setObject:action.callback forKey:@(tag)];
  429. return actionButton;
  430. }
  431. - (void)buttonHighlightedEnter:(UIButton *)sender {
  432. sender.backgroundColor = TUIChatDynamicColor(@"", @"#006EFF19");
  433. }
  434. - (void)buttonHighlightedExit:(UIButton *)sender {
  435. sender.backgroundColor = [UIColor clearColor];
  436. }
  437. - (void)onClick:(UIButton *)button {
  438. if (![self.actionCallback.allKeys containsObject:@(button.tag)]) {
  439. [self hideWithAnimation];
  440. return;
  441. }
  442. __weak typeof(self) weakSelf = self;
  443. [self hideByClickButton:button
  444. callback:^() {
  445. __strong typeof(weakSelf) strongSelf = weakSelf;
  446. TUIChatPopMenuActionCallback callback = [strongSelf.actionCallback objectForKey:@(button.tag)];
  447. if (callback) {
  448. callback();
  449. }
  450. }];
  451. }
  452. - (NSMutableArray *)actions {
  453. if (_actions == nil) {
  454. _actions = [NSMutableArray array];
  455. }
  456. return _actions;
  457. }
  458. - (NSMutableDictionary *)actionCallback {
  459. if (_actionCallback == nil) {
  460. _actionCallback = [NSMutableDictionary dictionary];
  461. }
  462. return _actionCallback;
  463. }
  464. // MARK: V2TIMAdvancedMsgListener
  465. - (void)onRecvMessageRevoked:(NSString *)msgID operateUser:(V2TIMUserFullInfo *)operateUser reason:(NSString *)reason {
  466. if ([msgID isEqualToString:self.targetCellData.msgID]) {
  467. [self hideWithAnimation];
  468. }
  469. }
  470. // MARK: ThemeChanged
  471. - (void)applyBorderTheme {
  472. if (_arrowLayer) {
  473. _arrowLayer.fillColor = TUIChatDynamicColor(@"chat_pop_menu_bg_color", @"#FFFFFF").CGColor;
  474. }
  475. }
  476. - (void)onThemeChanged {
  477. [self applyBorderTheme];
  478. }
  479. @end