TUIVoiceMessageCell.m 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. //
  2. // TUIVoiceMessageCell.m
  3. // UIKit
  4. //
  5. // Created by annidyfeng on 2019/5/30.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIVoiceMessageCell.h"
  9. #import <TIMCommon/TIMDefine.h>
  10. #import <TUICore/TUIThemeManager.h>
  11. #import <TUICore/TUICore.h>
  12. @implementation TUIVoiceMessageCell
  13. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  14. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  15. if (self) {
  16. _voice = [[UIImageView alloc] init];
  17. _voice.animationDuration = 1;
  18. [self.bubbleView addSubview:_voice];
  19. _duration = [[UILabel alloc] init];
  20. _duration.font = [UIFont boldSystemFontOfSize:12];
  21. [self.bubbleView addSubview:_duration];
  22. self.bottomContainer = [[UIView alloc] init];
  23. [self.contentView addSubview:self.bottomContainer];
  24. _voiceReadPoint = [[UIImageView alloc] init];
  25. _voiceReadPoint.backgroundColor = [UIColor redColor];
  26. _voiceReadPoint.frame = CGRectMake(0, 0, 5, 5);
  27. _voiceReadPoint.hidden = YES;
  28. [_voiceReadPoint.layer setCornerRadius:_voiceReadPoint.frame.size.width / 2];
  29. [_voiceReadPoint.layer setMasksToBounds:YES];
  30. [self.bubbleView addSubview:_voiceReadPoint];
  31. }
  32. return self;
  33. }
  34. - (void)prepareForReuse {
  35. [super prepareForReuse];
  36. for (UIView *view in self.bottomContainer.subviews) {
  37. [view removeFromSuperview];
  38. }
  39. }
  40. // Override
  41. - (void)notifyBottomContainerReadyOfData:(TUIMessageCellData *)cellData {
  42. NSDictionary *param = @{TUICore_TUIChatExtension_BottomContainer_CellData : self.voiceData};
  43. [TUICore raiseExtension:TUICore_TUIChatExtension_BottomContainer_ClassicExtensionID parentView:self.bottomContainer param:param];
  44. }
  45. - (void)fillWithData:(TUIVoiceMessageCellData *)data {
  46. // set data
  47. [super fillWithData:data];
  48. self.voiceData = data;
  49. self.bottomContainer.hidden = CGSizeEqualToSize(data.bottomContainerSize, CGSizeZero);
  50. if (data.duration > 0) {
  51. _duration.text = [NSString stringWithFormat:@"%ld\"", (long)data.duration];
  52. } else {
  53. _duration.text = @"1\"";
  54. }
  55. _voice.image = data.voiceImage;
  56. _voice.animationImages = data.voiceAnimationImages;
  57. BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
  58. if (hasRiskContent) {
  59. self.securityStrikeView.textLabel.text = TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrikeVoice);
  60. }
  61. if (self.voiceData.innerMessage.localCustomInt == 0 && self.voiceData.direction == MsgDirectionIncoming) self.voiceReadPoint.hidden = NO;
  62. // animate
  63. @weakify(self);
  64. [[RACObserve(data, isPlaying) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *x) {
  65. @strongify(self);
  66. if ([x boolValue]) {
  67. [self.voice startAnimating];
  68. } else {
  69. [self.voice stopAnimating];
  70. }
  71. }];
  72. [self applyStyleFromDirection:data.direction];
  73. // tell constraints they need updating
  74. [self setNeedsUpdateConstraints];
  75. // update constraints now so we can animate the change
  76. [self updateConstraintsIfNeeded];
  77. [self layoutIfNeeded];
  78. }
  79. - (void)applyStyleFromDirection:(TMsgDirection)direction {
  80. if (direction == MsgDirectionIncoming) {
  81. _duration.rtlAlignment = TUITextRTLAlignmentLeading;
  82. _duration.textColor = TUIChatDynamicColor(@"chat_voice_message_recv_duration_time_color", @"#000000");
  83. } else {
  84. _duration.rtlAlignment = TUITextRTLAlignmentTrailing;
  85. _duration.textColor = TUIChatDynamicColor(@"chat_voice_message_send_duration_time_color", @"#000000");
  86. }
  87. }
  88. + (BOOL)requiresConstraintBasedLayout {
  89. return YES;
  90. }
  91. // This is Apple's recommended place for adding/updating constraints
  92. - (void)updateConstraints {
  93. [super updateConstraints];
  94. [self.voice sizeToFit];
  95. [self.voice mas_remakeConstraints:^(MASConstraintMaker *make) {
  96. make.top.mas_equalTo(self.voiceData.voiceTop);
  97. make.width.height.mas_equalTo(_voiceData.voiceHeight);
  98. if (self.voiceData.direction == MsgDirectionOutgoing) {
  99. make.trailing.mas_equalTo(-self.voiceData.cellLayout.bubbleInsets.right);
  100. } else {
  101. make.leading.mas_equalTo(self.voiceData.cellLayout.bubbleInsets.left);
  102. }
  103. }];
  104. [self.duration mas_remakeConstraints:^(MASConstraintMaker *make) {
  105. make.width.mas_greaterThanOrEqualTo(10);
  106. make.height.mas_greaterThanOrEqualTo(TVoiceMessageCell_Duration_Size.height);
  107. make.centerY.mas_equalTo(self.voice);
  108. if (self.voiceData.direction == MsgDirectionOutgoing) {
  109. make.trailing.mas_equalTo(self.voice.mas_leading).mas_offset(-5);
  110. } else {
  111. make.leading.mas_equalTo(self.voice.mas_trailing).mas_offset(5);
  112. }
  113. }];
  114. if (self.voiceData.direction == MsgDirectionOutgoing) {
  115. self.voiceReadPoint.hidden = YES;
  116. } else {
  117. [self.voiceReadPoint mas_remakeConstraints:^(MASConstraintMaker *make) {
  118. make.top.mas_equalTo(self.bubbleView);
  119. make.leading.mas_equalTo(self.bubbleView.mas_trailing).mas_offset(1);
  120. make.size.mas_equalTo(CGSizeMake(5, 5));
  121. }];
  122. }
  123. BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
  124. if (hasRiskContent) {
  125. [self.securityStrikeView mas_remakeConstraints:^(MASConstraintMaker *make) {
  126. make.top.mas_equalTo(self.voice.mas_bottom);
  127. make.width.mas_equalTo(self.bubbleView);
  128. make.bottom.mas_equalTo(self.container).mas_offset(- self.messageData.messageContainerAppendSize.height);
  129. }];
  130. }
  131. [self layoutBottomContainer];
  132. }
  133. - (void)layoutBottomContainer {
  134. if (CGSizeEqualToSize(self.voiceData.bottomContainerSize, CGSizeZero)) {
  135. return;
  136. }
  137. CGSize size = self.voiceData.bottomContainerSize;
  138. [self.bottomContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
  139. if (self.voiceData.direction == MsgDirectionIncoming) {
  140. make.leading.mas_equalTo(self.container.mas_leading);
  141. } else {
  142. make.trailing.mas_equalTo(self.container.mas_trailing);
  143. }
  144. make.top.mas_equalTo(self.container.mas_bottom).offset(6);
  145. make.size.mas_equalTo(size);
  146. }];
  147. CGFloat repliesBtnTextWidth = self.messageModifyRepliesButton.frame.size.width;
  148. if (!self.messageModifyRepliesButton.hidden) {
  149. [self.messageModifyRepliesButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  150. if (self.voiceData.direction == MsgDirectionIncoming) {
  151. make.leading.mas_equalTo(self.container.mas_leading);
  152. } else {
  153. make.trailing.mas_equalTo(self.container.mas_trailing);
  154. }
  155. make.top.mas_equalTo(self.bottomContainer.mas_bottom);
  156. make.size.mas_equalTo(CGSizeMake(repliesBtnTextWidth + 10, 30));
  157. }];
  158. }
  159. }
  160. #pragma mark - TUIMessageCellProtocol
  161. + (CGFloat)getHeight:(TUIMessageCellData *)data withWidth:(CGFloat)width {
  162. CGFloat height = [super getHeight:data withWidth:width];
  163. if (data.bottomContainerSize.height > 0) {
  164. height += data.bottomContainerSize.height + kScale375(6);
  165. }
  166. return height;
  167. }
  168. + (CGSize)getContentSize:(TUIMessageCellData *)data {
  169. TUIVoiceMessageCellData *voiceCellData = (TUIVoiceMessageCellData *)data;
  170. CGFloat bubbleWidth = TVoiceMessageCell_Back_Width_Min + voiceCellData.duration / TVoiceMessageCell_Max_Duration * Screen_Width;
  171. if (bubbleWidth > TVoiceMessageCell_Back_Width_Max) {
  172. bubbleWidth = TVoiceMessageCell_Back_Width_Max;
  173. }
  174. CGFloat bubbleHeight = TVoiceMessageCell_Duration_Size.height;
  175. if (voiceCellData.direction == MsgDirectionIncoming) {
  176. bubbleWidth = MAX(bubbleWidth, [TUIBubbleMessageCell incommingBubble].size.width);
  177. bubbleHeight = voiceCellData.voiceImage.size.height + 2 * voiceCellData.voiceTop; //[TUIBubbleMessageCellData incommingBubble].size.height;
  178. } else {
  179. bubbleWidth = MAX(bubbleWidth, [TUIBubbleMessageCell outgoingBubble].size.width);
  180. bubbleHeight = voiceCellData.voiceImage.size.height + 2 * voiceCellData.voiceTop; // [TUIBubbleMessageCellData outgoingBubble].size.height;
  181. }
  182. CGFloat width = bubbleWidth + TVoiceMessageCell_Duration_Size.width;
  183. CGFloat height = bubbleHeight;
  184. BOOL hasRiskContent = voiceCellData.innerMessage.hasRiskContent;
  185. if (hasRiskContent) {
  186. width = MAX(width, 200);// width must more than TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrikeVoice)
  187. height += kTUISecurityStrikeViewTopLineMargin;
  188. height += kTUISecurityStrikeViewTopLineToBottom;
  189. }
  190. return CGSizeMake(width, height);
  191. }
  192. @end