TUIImageMessageCell.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. //
  2. // TUIImageMessageCell.m
  3. // UIKit
  4. //
  5. // Created by annidyfeng on 2019/5/30.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIImageMessageCell.h"
  9. #import <TIMCommon/TIMDefine.h>
  10. @interface TUIImageMessageCell ()
  11. @property(nonatomic, strong) UIView *animateHighlightView;
  12. @end
  13. @implementation TUIImageMessageCell
  14. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  15. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  16. if (self) {
  17. _thumb = [[UIImageView alloc] init];
  18. _thumb.layer.cornerRadius = 5.0;
  19. [_thumb.layer setMasksToBounds:YES];
  20. _thumb.contentMode = UIViewContentModeScaleAspectFit;
  21. _thumb.backgroundColor = [UIColor clearColor];
  22. [self.container addSubview:_thumb];
  23. _progress = [[UILabel alloc] init];
  24. _progress.textColor = [UIColor whiteColor];
  25. _progress.font = [UIFont systemFontOfSize:15];
  26. _progress.textAlignment = NSTextAlignmentCenter;
  27. _progress.layer.cornerRadius = 5.0;
  28. _progress.hidden = YES;
  29. _progress.backgroundColor = TImageMessageCell_Progress_Color;
  30. [_progress.layer setMasksToBounds:YES];
  31. [self.container addSubview:_progress];
  32. [self makeConstraints];
  33. }
  34. return self;
  35. }
  36. - (void)fillWithData:(TUIImageMessageCellData *)data;
  37. {
  38. // set data
  39. [super fillWithData:data];
  40. self.imageData = data;
  41. _thumb.image = nil;
  42. BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
  43. if (hasRiskContent) {
  44. self.thumb.image = TIMCommonBundleThemeImage(@"", @"icon_security_strike");
  45. self.securityStrikeView.textLabel.text = TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrikeImage);
  46. self.progress.hidden = YES;
  47. return;
  48. }
  49. if (data.thumbImage == nil) {
  50. [data downloadImage:TImage_Type_Thumb];
  51. }
  52. @weakify(self);
  53. [[RACObserve(data, thumbImage) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(UIImage *thumbImage) {
  54. @strongify(self);
  55. if (thumbImage) {
  56. self.thumb.image = thumbImage;
  57. }
  58. }];
  59. [[[RACObserve(data, thumbProgress) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSNumber *x) {
  60. @strongify(self);
  61. int progress = [x intValue];
  62. self.progress.text = [NSString stringWithFormat:@"%d%%", progress];
  63. self.progress.hidden = (progress >= 100 || progress == 0);
  64. }];
  65. // tell constraints they need updating
  66. [self setNeedsUpdateConstraints];
  67. // update constraints now so we can animate the change
  68. [self updateConstraintsIfNeeded];
  69. [self layoutIfNeeded];
  70. }
  71. + (BOOL)requiresConstraintBasedLayout {
  72. return YES;
  73. }
  74. - (void)makeConstraints {
  75. [self.thumb mas_makeConstraints:^(MASConstraintMaker *make) {
  76. make.height.mas_equalTo(self.bubbleView);
  77. make.width.mas_equalTo(self.bubbleView);
  78. make.top.mas_equalTo(self.bubbleView);
  79. make.leading.mas_equalTo(self.bubbleView);
  80. }];
  81. [self.progress mas_makeConstraints:^(MASConstraintMaker *make) {
  82. make.edges.mas_equalTo(self.bubbleView);
  83. }];
  84. }
  85. // this is Apple's recommended place for adding/updating constraints
  86. - (void)updateConstraints {
  87. [super updateConstraints];
  88. if (self.imageData.isSuperLongImage) {
  89. self.thumb.contentMode = UIViewContentModeScaleToFill;
  90. }
  91. else {
  92. self.thumb.contentMode = UIViewContentModeScaleAspectFit;
  93. }
  94. CGFloat topMargin = 0;
  95. CGFloat height = self.bubbleView.mm_h;
  96. if (self.messageData.messageContainerAppendSize.height > 0) {
  97. topMargin = 10;
  98. CGFloat tagViewTopMargin = 6;
  99. height = self.bubbleView.mm_h - topMargin - self.messageData.messageContainerAppendSize.height - tagViewTopMargin;
  100. [self.thumb mas_remakeConstraints:^(MASConstraintMaker *make) {
  101. make.height.mas_equalTo(height);
  102. make.width.mas_equalTo(self.bubbleView.mas_width);
  103. make.top.mas_equalTo(self.bubbleView).mas_offset(topMargin);
  104. make.leading.mas_equalTo(self.bubbleView);
  105. }];
  106. } else {
  107. [self.thumb mas_remakeConstraints:^(MASConstraintMaker *make) {
  108. make.top.mas_equalTo(self.bubbleView).mas_offset(self.messageData.cellLayout.bubbleInsets.top);
  109. make.bottom.mas_equalTo(self.bubbleView).mas_offset(-self.messageData.cellLayout.bubbleInsets.bottom);
  110. make.leading.mas_equalTo(self.bubbleView).mas_offset(self.messageData.cellLayout.bubbleInsets.left);
  111. make.trailing.mas_equalTo(self.bubbleView).mas_offset(-self.messageData.cellLayout.bubbleInsets.right);
  112. }];
  113. }
  114. BOOL hasRiskContent = self.messageData.innerMessage.hasRiskContent;
  115. if (hasRiskContent ) {
  116. [self.thumb mas_remakeConstraints:^(MASConstraintMaker *make) {
  117. make.top.mas_equalTo(self.bubbleView).mas_offset(12);
  118. make.size.mas_equalTo(CGSizeMake(150, 150));
  119. make.centerX.mas_equalTo(self.bubbleView);
  120. }];
  121. [self.securityStrikeView mas_remakeConstraints:^(MASConstraintMaker *make) {
  122. make.top.mas_equalTo(self.thumb.mas_bottom);
  123. make.width.mas_equalTo(self.bubbleView);
  124. make.bottom.mas_equalTo(self.container).mas_offset(- self.messageData.messageContainerAppendSize.height);
  125. }];
  126. }
  127. [self.progress mas_remakeConstraints:^(MASConstraintMaker *make) {
  128. make.edges.mas_equalTo(self.bubbleView);
  129. }];
  130. [self.selectedView mas_remakeConstraints:^(MASConstraintMaker *make) {
  131. make.edges.mas_equalTo(self.contentView);
  132. }];
  133. [self.selectedIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
  134. make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(3);
  135. make.top.mas_equalTo(self.avatarView.mas_centerY).mas_offset(-10);
  136. if (self.messageData.showCheckBox) {
  137. make.width.mas_equalTo(20);
  138. make.height.mas_equalTo(20);
  139. } else {
  140. make.size.mas_equalTo(CGSizeZero);
  141. }
  142. }];
  143. [self.timeLabel sizeToFit];
  144. [self.timeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  145. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-10);
  146. make.top.mas_equalTo(self.avatarView);
  147. if (self.messageData.showMessageTime) {
  148. make.width.mas_equalTo(self.timeLabel.frame.size.width);
  149. make.height.mas_equalTo(self.timeLabel.frame.size.height);
  150. } else {
  151. make.width.mas_equalTo(0);
  152. make.height.mas_equalTo(0);
  153. }
  154. }];
  155. }
  156. - (void)layoutSubviews {
  157. [super layoutSubviews];
  158. }
  159. - (void)highlightWhenMatchKeyword:(NSString *)keyword {
  160. if (keyword) {
  161. if (self.highlightAnimating) {
  162. return;
  163. }
  164. [self animate:3];
  165. }
  166. }
  167. - (void)animate:(int)times {
  168. times--;
  169. if (times < 0) {
  170. [self.animateHighlightView removeFromSuperview];
  171. self.highlightAnimating = NO;
  172. return;
  173. }
  174. self.highlightAnimating = YES;
  175. self.animateHighlightView.frame = self.container.bounds;
  176. self.animateHighlightView.alpha = 0.1;
  177. [self.container addSubview:self.animateHighlightView];
  178. [UIView animateWithDuration:0.25
  179. animations:^{
  180. self.animateHighlightView.alpha = 0.5;
  181. }
  182. completion:^(BOOL finished) {
  183. [UIView animateWithDuration:0.25
  184. animations:^{
  185. self.animateHighlightView.alpha = 0.1;
  186. }
  187. completion:^(BOOL finished) {
  188. if (!self.imageData.highlightKeyword) {
  189. [self animate:0];
  190. return;
  191. }
  192. [self animate:times];
  193. }];
  194. }];
  195. }
  196. - (UIView *)animateHighlightView {
  197. if (_animateHighlightView == nil) {
  198. _animateHighlightView = [[UIView alloc] init];
  199. _animateHighlightView.backgroundColor = [UIColor orangeColor];
  200. }
  201. return _animateHighlightView;
  202. }
  203. #pragma mark - TUIMessageCellProtocol
  204. + (CGFloat)getEstimatedHeight:(TUIMessageCellData *)data {
  205. return 186.f;
  206. }
  207. + (CGSize)getContentSize:(TUIMessageCellData *)data {
  208. NSAssert([data isKindOfClass:TUIImageMessageCellData.class], @"data must be kind of TUIImageMessageCellData");
  209. TUIImageMessageCellData *imageCellData = (TUIImageMessageCellData *)data;
  210. CGSize size = CGSizeZero;
  211. BOOL isDir = NO;
  212. if (![imageCellData.path isEqualToString:@""] && [[NSFileManager defaultManager] fileExistsAtPath:imageCellData.path isDirectory:&isDir]) {
  213. if (!isDir) {
  214. size = [UIImage imageWithContentsOfFile:imageCellData.path].size;
  215. }
  216. }
  217. if (CGSizeEqualToSize(size, CGSizeZero)) {
  218. for (TUIImageItem *item in imageCellData.items) {
  219. if (item.type == TImage_Type_Thumb) {
  220. size = item.size;
  221. }
  222. }
  223. }
  224. if (CGSizeEqualToSize(size, CGSizeZero)) {
  225. for (TUIImageItem *item in imageCellData.items) {
  226. if (item.type == TImage_Type_Large) {
  227. size = item.size;
  228. }
  229. }
  230. }
  231. if (CGSizeEqualToSize(size, CGSizeZero)) {
  232. for (TUIImageItem *item in imageCellData.items) {
  233. if (item.type == TImage_Type_Origin) {
  234. size = item.size;
  235. }
  236. }
  237. }
  238. if (CGSizeEqualToSize(size, CGSizeZero)) {
  239. return size;
  240. }
  241. if (size.height > size.width) {
  242. if (size.height > 5* size.width) {
  243. size.width = TImageMessageCell_Image_Width_Max;
  244. size.height = TImageMessageCell_Image_Height_Max;
  245. imageCellData.isSuperLongImage = YES;
  246. }
  247. else {
  248. size.width = size.width / size.height * TImageMessageCell_Image_Height_Max;
  249. size.height = TImageMessageCell_Image_Height_Max;
  250. }
  251. } else {
  252. size.height = size.height / size.width * TImageMessageCell_Image_Width_Max;
  253. size.width = TImageMessageCell_Image_Width_Max;
  254. }
  255. BOOL hasRiskContent = imageCellData.innerMessage.hasRiskContent;
  256. if (hasRiskContent) {
  257. CGFloat bubbleTopMargin = 12;
  258. CGFloat bubbleBottomMargin = 12;
  259. size.height = MAX(size.height, 150);// width must more than TIMCommonBundleThemeImage(@"", @"icon_security_strike");
  260. size.width = MAX(size.width, 200);// width must more than TIMCommonLocalizableString(TUIKitMessageTypeSecurityStrike)
  261. size.height += bubbleTopMargin;
  262. size.height += kTUISecurityStrikeViewTopLineMargin;
  263. size.height += kTUISecurityStrikeViewTopLineToBottom;
  264. size.height += bubbleBottomMargin;
  265. }
  266. return size;
  267. }
  268. @end