TUIConversationCell.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. //
  2. // TUIConversationCell.m
  3. // TXIMSDK_TUIKit_iOS
  4. //
  5. // Created by annidyfeng on 2019/5/16.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIConversationCell.h"
  9. #import <TIMCommon/TIMCommonModel.h>
  10. #import <TIMCommon/TIMDefine.h>
  11. #import <TUICore/TUIThemeManager.h>
  12. #import <TUICore/TUITool.h>
  13. #import "TUIConversationConfig.h"
  14. #import <TIMCommon/MOHeadNormalView.h>
  15. #import <YYKit/YYLabel.h>
  16. #define kScale UIScreen.mainScreen.bounds.size.width / 375.0
  17. @interface TUIConversationCell ()
  18. @property (nonatomic, strong) MOHeadNormalView *headBgView;//头像框
  19. @property (nonatomic, strong) YYLabel *nameTagLab;
  20. @property (nonatomic, strong) UIView *lineView;//E1E2E8
  21. @end
  22. @implementation TUIConversationCell
  23. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  24. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  25. if (self) {
  26. self.backgroundColor = [UIColor clearColor];
  27. self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
  28. _headImageView = [[UIImageView alloc] init];
  29. [self.contentView addSubview:_headImageView];
  30. [self.contentView addSubview:self.headBgView];
  31. _timeLabel = [[UILabel alloc] init];
  32. _timeLabel.font = [UIFont systemFontOfSize:12];
  33. _timeLabel.textColor = TIMCommonDynamicColor(@"form_desc_color", @"#878A99");;
  34. _timeLabel.layer.masksToBounds = YES;
  35. [_timeLabel setRtlAlignment:TUITextRTLAlignmentLeading];
  36. [self.contentView addSubview:_timeLabel];
  37. _titleLabel = [[UILabel alloc] init];
  38. _titleLabel.font = [UIFont systemFontOfSize:16];
  39. _titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#17171A");
  40. _titleLabel.layer.masksToBounds = YES;
  41. [_titleLabel setRtlAlignment:TUITextRTLAlignmentLeading];
  42. [self.contentView addSubview:_titleLabel];
  43. [self.contentView addSubview:self.nameTagLab];
  44. _unReadView = [[TUIUnReadView alloc] init];
  45. [self.contentView addSubview:_unReadView];
  46. _subTitleLabel = [[UILabel alloc] init];
  47. _subTitleLabel.layer.masksToBounds = YES;
  48. _subTitleLabel.font = [UIFont systemFontOfSize:14];
  49. // _subTitleLabel.textColor = TIMCommonDynamicColor(@"form_subtitle_color", @"#878A99");
  50. [_subTitleLabel setRtlAlignment:TUITextRTLAlignmentLeading];
  51. [self.contentView addSubview:_subTitleLabel];
  52. _notDisturbRedDot = [[UIView alloc] init];
  53. _notDisturbRedDot.backgroundColor = [UIColor redColor];
  54. _notDisturbRedDot.layer.cornerRadius = TConversationCell_Margin_Disturb_Dot / 2.0;
  55. _notDisturbRedDot.layer.masksToBounds = YES;
  56. [self.contentView addSubview:_notDisturbRedDot];
  57. _notDisturbView = [[UIImageView alloc] init];
  58. [self.contentView addSubview:_notDisturbView];
  59. [self.contentView addSubview:self.lineView];
  60. [self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
  61. make.bottom.right.equalTo(self.contentView);
  62. make.left.equalTo(self.contentView).offset(82.0);
  63. make.height.equalTo(@0.5);
  64. }];
  65. [self setSeparatorInset:UIEdgeInsetsMake(0, TConversationCell_Margin, 0, 0)];
  66. [self setSelectionStyle:UITableViewCellSelectionStyleNone];
  67. //[self setSelectionStyle:UITableViewCellSelectionStyleDefault];
  68. // selectedIcon
  69. _selectedIcon = [[UIImageView alloc] init];
  70. [self.contentView addSubview:_selectedIcon];
  71. _onlineStatusIcon = [[UIImageView alloc] init];
  72. [self.contentView addSubview:_onlineStatusIcon];
  73. _lastMessageStatusImageView = [[UIImageView alloc] init];
  74. [self.contentView addSubview:_lastMessageStatusImageView];
  75. _lastMessageStatusImageView.hidden = YES;
  76. }
  77. return self;
  78. }
  79. - (void)fillWithData:(TUIConversationCellData *)convData {
  80. self.convData = convData;
  81. self.titleLabel.textColor = TIMCommonDynamicColor(@"form_title_color", @"#000000");
  82. if ([TUIConversationConfig sharedConfig].cellTitleLabelFont) {
  83. self.titleLabel.font = [TUIConversationConfig sharedConfig].cellTitleLabelFont;
  84. }
  85. self.subTitleLabel.attributedText = convData.subTitle;
  86. if ([TUIConversationConfig sharedConfig].cellSubtitleLabelFont) {
  87. self.subTitleLabel.font = [TUIConversationConfig sharedConfig].cellSubtitleLabelFont;
  88. }
  89. // self.timeLabel.text = [TUITool convertDateToStr:convData.time];
  90. self.timeLabel.text = [TUIConversationCell convertDateTwoToStr:convData.time];
  91. if ([TUIConversationConfig sharedConfig].cellTimeLabelFont) {
  92. self.timeLabel.font = [TUIConversationConfig sharedConfig].cellTimeLabelFont;
  93. }
  94. if (self.convData.showCheckBox) {
  95. _selectedIcon.hidden = NO;
  96. } else {
  97. _selectedIcon.hidden = YES;
  98. }
  99. [self configRedPoint:convData];
  100. if (convData.isOnTop) {
  101. self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_top_bg_color", @"#F4F4F4");
  102. } else {
  103. self.contentView.backgroundColor = TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
  104. ;
  105. }
  106. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  107. self.headImageView.layer.masksToBounds = YES;
  108. self.headImageView.layer.cornerRadius = self.headImageView.frame.size.height / 2;
  109. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  110. self.headImageView.layer.masksToBounds = YES;
  111. self.headImageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  112. }
  113. @weakify(self);
  114. [[[RACObserve(convData, title) takeUntil:self.rac_prepareForReuseSignal] distinctUntilChanged] subscribeNext:^(NSString *x) {
  115. @strongify(self);
  116. self.titleLabel.text = x;
  117. // tell constraints they need updating
  118. [self setNeedsUpdateConstraints];
  119. // update constraints now so we can animate the change
  120. [self updateConstraintsIfNeeded];
  121. [self layoutIfNeeded];
  122. }];
  123. /**
  124. *
  125. * Setup default avatar
  126. */
  127. if (convData.groupID.length > 0) {
  128. /**
  129. * ,
  130. * If it is a group, change the group default avatar to the last used avatar
  131. */
  132. UIImage *avatar = nil;
  133. if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
  134. NSString *key = [NSString stringWithFormat:@"TUIConversationLastGroupMember_%@", convData.groupID];
  135. NSInteger member = [NSUserDefaults.standardUserDefaults integerForKey:key];
  136. avatar = [TUIGroupAvatar getCacheAvatarForGroup:convData.groupID number:(UInt32)member];
  137. }
  138. convData.avatarImage = avatar ? avatar : DefaultGroupAvatarImageByGroupType(convData.groupType);
  139. }
  140. [[RACObserve(convData, faceUrl) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(NSString *faceUrl) {
  141. @strongify(self);
  142. if (self.convData.groupID.length > 0) {
  143. /**
  144. *
  145. * Group avatar
  146. */
  147. if (IS_NOT_EMPTY_NSSTRING(faceUrl)) {
  148. /**
  149. * The group avatar has been manually set externally
  150. */
  151. [self.headImageView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:self.convData.avatarImage];
  152. } else {
  153. /**
  154. * The group avatar has not been set externally. If the synthetic avatar is allowed, the synthetic avatar will be used; otherwise, the default
  155. * avatar will be used.
  156. */
  157. if (TUIConfig.defaultConfig.enableGroupGridAvatar) {
  158. /**
  159. * If the synthetic avatar is allowed, the synthetic avatar will be used
  160. * 1. Asynchronously obtain the cached synthetic avatar according to the number of group members
  161. * 2. If the cache is hit, use the cached synthetic avatar directly
  162. * 3. If the cache is not hit, recompose a new avatar
  163. *
  164. * Note:
  165. * 1. Since "asynchronously obtaining cached avatars" and "synthesizing avatars" take a long time, it is easy to cause cell reuse problems, so
  166. * it is necessary to confirm whether to assign values directly according to groupID.
  167. * 2. Use SDWebImage to implement placeholder, because SDWebImage has already dealt with the problem of cell reuse
  168. */
  169. // 1. Obtain group avatar from cache
  170. // fix: The getCacheGroupAvatar needs to request the
  171. // network. When the network is disconnected, since the headImageView is not set, the current conversation sends a message, the conversation
  172. // is moved up, and the avatar of the first conversation is reused, resulting in confusion of the avatar.
  173. [self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
  174. [TUIGroupAvatar
  175. getCacheGroupAvatar:convData.groupID
  176. callback:^(UIImage *avatar, NSString *groupID) {
  177. @strongify(self);
  178. if ([groupID isEqualToString:self.convData.groupID]) {
  179. // 1.1 When the callback is invoked, the cell is not reused
  180. if (avatar != nil) {
  181. // 2. Hit the cache and assign directly
  182. [self.headImageView sd_setImageWithURL:nil placeholderImage:avatar];
  183. } else {
  184. // 3. Synthesize new avatars asynchronously without hitting cache
  185. [self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
  186. [TUIGroupAvatar
  187. fetchGroupAvatars:convData.groupID
  188. placeholder:convData.avatarImage
  189. callback:^(BOOL success, UIImage *image, NSString *groupID) {
  190. @strongify(self);
  191. if ([groupID isEqualToString:self.convData.groupID]) {
  192. // callback ,cell
  193. // When the callback is invoked, the cell is not reused
  194. [self.headImageView
  195. sd_setImageWithURL:nil
  196. placeholderImage:success ? image
  197. : DefaultGroupAvatarImageByGroupType(self.convData.groupType)];
  198. } else {
  199. //When the callback is invoked, the cell has been reused to other groupIDs.
  200. // Since a new callback will be triggered when the new groupID synthesizes new avatar, it is
  201. // ignored here
  202. }
  203. }];
  204. }
  205. } else {
  206. // 1.2 When the callback is invoked, the cell has been reused to other groupIDs. Since a new callback will be triggered
  207. // when the new groupID gets the cache, it is ignored here
  208. }
  209. }];
  210. } else {
  211. /**
  212. * Synthetic avatars are not allowed, use the default avatar directly
  213. */
  214. [self.headImageView sd_setImageWithURL:nil placeholderImage:convData.avatarImage];
  215. }
  216. }
  217. } else {
  218. /**
  219. * Personal avatar
  220. */
  221. [self.headImageView sd_setImageWithURL:[NSURL URLWithString:faceUrl] placeholderImage:self.convData.avatarImage];
  222. }
  223. }];
  224. if (convData.showCheckBox) {
  225. NSString *imageName = nil;
  226. if (convData.disableSelected) {
  227. imageName = TIMCommonImagePath(@"icon_select_selected_disable");
  228. } else if (convData.selected) {
  229. imageName = TIMCommonImagePath(@"icon_select_selected");
  230. } else {
  231. imageName = TIMCommonImagePath(@"icon_select_normal");
  232. }
  233. self.selectedIcon.image = [UIImage imageNamed:imageName];
  234. }
  235. [self configOnlineStatusIcon:convData];
  236. [self configDisplayLastMessageStatusImage:convData];
  237. if(convData.headdress.length > 0){
  238. self.headBgView.hidden = NO;
  239. self.headBgView.isLiving = NO;
  240. self.headBgView.effectType = convData.headgearType;
  241. self.headBgView.imgUrlStr = convData.headdress;
  242. }
  243. else{
  244. self.headBgView.hidden = YES;
  245. }
  246. if(convData.nameAttrString){
  247. self.nameTagLab.hidden = NO;
  248. self.nameTagLab.attributedText = convData.nameAttrString;
  249. }
  250. else{
  251. self.nameTagLab.hidden = YES;
  252. }
  253. // tell constraints they need updating
  254. [self setNeedsUpdateConstraints];
  255. // update constraints now so we can animate the change
  256. [self updateConstraintsIfNeeded];
  257. [self layoutIfNeeded];
  258. }
  259. - (void)configDisplayLastMessageStatusImage:(TUIConversationCellData *)convData {
  260. UIImage *image = [self getDisplayLastMessageStatusImage:convData];
  261. self.lastMessageStatusImageView.image = image;
  262. }
  263. - (UIImage *)getDisplayLastMessageStatusImage:(TUIConversationCellData *)convData {
  264. UIImage *image = nil;
  265. if (!convData.draftText && (V2TIM_MSG_STATUS_SENDING == convData.lastMessage.status || V2TIM_MSG_STATUS_SEND_FAIL == convData.lastMessage.status)) {
  266. if (V2TIM_MSG_STATUS_SENDING == convData.lastMessage.status) {
  267. image = [UIImage imageNamed:TUIConversationImagePath(@"msg_sending_for_conv")];
  268. } else {
  269. image = [UIImage imageNamed:TUIConversationImagePath(@"msg_error_for_conv")];
  270. }
  271. }
  272. return image;
  273. }
  274. - (void)configOnlineStatusIcon:(TUIConversationCellData *)convData {
  275. @weakify(self);
  276. [[RACObserve(TUIConfig.defaultConfig, displayOnlineStatusIcon) takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(id _Nullable x) {
  277. @strongify(self);
  278. if (convData.onlineStatus == TUIConversationOnlineStatusOnline && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
  279. self.onlineStatusIcon.hidden = NO;
  280. self.onlineStatusIcon.image = TIMCommonDynamicImage(@"icon_online_status", [UIImage imageNamed:TIMCommonImagePath(@"icon_online_status")]);
  281. } else if (convData.onlineStatus == TUIConversationOnlineStatusOffline && TUIConfig.defaultConfig.displayOnlineStatusIcon) {
  282. self.onlineStatusIcon.hidden = NO;
  283. self.onlineStatusIcon.image = TIMCommonDynamicImage(@"icon_offline_status", [UIImage imageNamed:TIMCommonImagePath(@"icon_offline_status")]);
  284. } else {
  285. self.onlineStatusIcon.hidden = YES;
  286. self.onlineStatusIcon.image = nil;
  287. }
  288. }];
  289. }
  290. - (void)configRedPoint:(TUIConversationCellData *)convData {
  291. if (convData.isNotDisturb) {
  292. if (0 == convData.unreadCount) {
  293. self.notDisturbRedDot.hidden = YES;
  294. } else {
  295. self.notDisturbRedDot.hidden = NO;
  296. }
  297. self.notDisturbView.hidden = NO;
  298. self.unReadView.hidden = YES;
  299. UIImage *image = TUIConversationBundleThemeImage(@"conversation_message_not_disturb_img", @"message_not_disturb");
  300. [self.notDisturbView setImage:image];
  301. } else {
  302. self.notDisturbRedDot.hidden = YES;
  303. self.notDisturbView.hidden = YES;
  304. [self.unReadView setNum:convData.unreadCount];
  305. self.unReadView.hidden = convData.unreadCount == 0 ? YES : ![TUIConversationConfig sharedConfig].showCellUnreadCount;
  306. }
  307. // Mark As Unread
  308. if (convData.isMarkAsUnread) {
  309. // When marked as unread, don't care about 'unreadCount', you need to display red dot/number 1 according to whether do not disturb or not
  310. if (convData.isNotDisturb) {
  311. // Displays a red dot when marked as unread and do not disturb
  312. self.notDisturbRedDot.hidden = NO;
  313. } else {
  314. // Marked unread Show number 1
  315. [self.unReadView setNum:1];
  316. self.unReadView.hidden = ![TUIConversationConfig sharedConfig].showCellUnreadCount;
  317. }
  318. }
  319. // Collapsed group chat No need for Do Not Disturb icon
  320. if (convData.isLocalConversationFoldList) {
  321. self.notDisturbView.hidden = YES;
  322. }
  323. }
  324. + (BOOL)requiresConstraintBasedLayout {
  325. return YES;
  326. }
  327. // this is Apple's recommended place for adding/updating constraints
  328. - (void)updateConstraints {
  329. [super updateConstraints];
  330. CGFloat height = [self.convData heightOfWidth:self.mm_w];
  331. self.mm_h = height;
  332. if (self.convData.isOnTop) {
  333. self.contentView.backgroundColor = [TUIConversationConfig sharedConfig].pinnedCellBackgroundColor ? : TUIConversationDynamicColor(@"conversation_cell_top_bg_color", @"#F4F4F4");
  334. } else {
  335. self.contentView.backgroundColor = [TUIConversationConfig sharedConfig].cellBackgroundColor ? : TUIConversationDynamicColor(@"conversation_cell_bg_color", @"#FFFFFF");
  336. }
  337. CGFloat selectedIconSize = 20;
  338. [self.selectedIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
  339. if (self.convData.showCheckBox) {
  340. make.width.height.mas_equalTo(selectedIconSize);
  341. make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(10);
  342. make.centerY.mas_equalTo(self.contentView.mas_centerY);
  343. }
  344. }];
  345. MASAttachKeys(self.selectedIcon);
  346. CGFloat imgHeight = height - 2 * (TConversationCell_Margin);
  347. [self.headImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
  348. make.size.mas_equalTo(imgHeight);
  349. make.centerY.mas_equalTo(self.contentView.mas_centerY);
  350. if (self.convData.showCheckBox) {
  351. make.leading.mas_equalTo(self.selectedIcon.mas_trailing).mas_offset(TConversationCell_Margin + 3);
  352. }
  353. else {
  354. make.leading.mas_equalTo(self.contentView.mas_leading).mas_offset(TConversationCell_Margin + 14);
  355. }
  356. }];
  357. MASAttachKeys(self.headImageView);
  358. [self.headBgView mas_remakeConstraints:^(MASConstraintMaker *make) {
  359. make.centerX.equalTo(self.headImageView);
  360. make.centerY.equalTo(self.headImageView);
  361. make.width.height.equalTo(@48.0);
  362. }];
  363. self.headBgView.headImgWidth = 48.0;
  364. CGFloat titleLabWidth = [self getWidthWithString:self.titleLabel.text font:[UIFont systemFontOfSize:16.0 weight:UIFontWeightMedium]] + 1.0;
  365. if(titleLabWidth < 44.0){
  366. titleLabWidth = 44.0;
  367. }
  368. if(titleLabWidth > 80.0){
  369. titleLabWidth = 80.0;
  370. }
  371. if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRounded) {
  372. self.headImageView.layer.masksToBounds = YES;
  373. self.headImageView.layer.cornerRadius = imgHeight / 2;
  374. } else if ([TUIConfig defaultConfig].avatarType == TAvatarTypeRadiusCorner) {
  375. self.headImageView.layer.masksToBounds = YES;
  376. self.headImageView.layer.cornerRadius = [TUIConfig defaultConfig].avatarCornerRadius;
  377. }
  378. CGFloat titleLabelHeight = 30;
  379. if (self.convData.isLiteMode) {
  380. [self.titleLabel sizeToFit];
  381. [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  382. make.width.mas_greaterThanOrEqualTo(120);
  383. make.height.mas_greaterThanOrEqualTo(titleLabelHeight);
  384. make.top.mas_equalTo((height - titleLabelHeight) / 2);
  385. make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(20);
  386. make.trailing.mas_equalTo(self.contentView).mas_offset(- 2*TConversationCell_Margin_Text);
  387. }];
  388. self.timeLabel.hidden = YES;
  389. self.lastMessageStatusImageView.hidden = YES;
  390. self.subTitleLabel.hidden = YES;
  391. self.unReadView.hidden = YES;
  392. self.notDisturbRedDot.hidden = YES;
  393. self.notDisturbView.hidden = YES;
  394. self.onlineStatusIcon.hidden = YES;
  395. } else {
  396. [self.timeLabel sizeToFit];
  397. [self.timeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  398. make.width.mas_equalTo(self.timeLabel);
  399. make.height.mas_greaterThanOrEqualTo(self.timeLabel.font.lineHeight);
  400. make.top.mas_equalTo(self.contentView.mas_top).mas_offset(TConversationCell_Margin_Text);
  401. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(-28);
  402. }];
  403. MASAttachKeys(self.timeLabel);
  404. [self.lastMessageStatusImageView mas_remakeConstraints:^(MASConstraintMaker *make) {
  405. make.width.mas_equalTo(kScale390(14));
  406. make.height.mas_equalTo(14);
  407. make.trailing.mas_equalTo(self.contentView.mas_trailing).mas_offset(- (kScale390(1) + TConversationCell_Margin_Disturb + kScale390(8)));
  408. make.bottom.mas_equalTo(self.contentView.mas_bottom).mas_offset(kScale390(16));
  409. }];
  410. MASAttachKeys(self.lastMessageStatusImageView);
  411. [self.titleLabel sizeToFit];
  412. [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  413. make.height.mas_greaterThanOrEqualTo(titleLabelHeight);
  414. make.top.mas_equalTo(self.contentView.mas_top).mas_offset(TConversationCell_Margin_Text - 5);
  415. make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(20);
  416. // make.trailing.mas_lessThanOrEqualTo(self.timeLabel.mas_trailing).mas_offset(- 2*TConversationCell_Margin_Text);
  417. make.width.equalTo(@(titleLabWidth));
  418. }];
  419. MASAttachKeys(self.titleLabel);
  420. [self.subTitleLabel sizeToFit];
  421. [self.subTitleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  422. make.height.mas_greaterThanOrEqualTo(self.subTitleLabel);
  423. make.bottom.mas_equalTo(self.contentView).mas_offset(- TConversationCell_Margin_Text);
  424. make.leading.mas_equalTo(self.titleLabel);
  425. make.trailing.mas_equalTo(self.contentView).mas_offset(- 2*TConversationCell_Margin_Text);
  426. }];
  427. MASAttachKeys(self.subTitleLabel);
  428. [self.nameTagLab mas_remakeConstraints:^(MASConstraintMaker *make) {
  429. make.leading.equalTo(self.titleLabel.mas_trailing).mas_offset(3);
  430. make.centerY.equalTo(self.titleLabel.mas_centerY);
  431. make.height.mas_equalTo(20);
  432. make.width.mas_greaterThanOrEqualTo(20);
  433. make.trailing.mas_lessThanOrEqualTo(self.timeLabel.mas_trailing).mas_offset(- 2*TConversationCell_Margin_Text);
  434. }];
  435. [self.unReadView.unReadLabel sizeToFit];
  436. NSDictionary *attrs = @{NSFontAttributeName:[UIFont systemFontOfSize:10]};
  437. CGFloat tempWidth = [self.unReadView.unReadLabel.text boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size.width + 5.0;
  438. if(tempWidth < kScale375(14)){
  439. tempWidth = kScale375(14);
  440. }
  441. [self.unReadView mas_remakeConstraints:^(MASConstraintMaker *make) {
  442. make.trailing.mas_equalTo(self.timeLabel.mas_trailing);
  443. make.centerY.mas_equalTo(self.headImageView.mas_centerY).mas_offset(8);
  444. make.width.mas_equalTo(tempWidth);
  445. make.height.mas_equalTo(kScale375(14));
  446. }];
  447. MASAttachKeys(self.unReadView);
  448. [self.unReadView.unReadLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
  449. make.center.mas_equalTo(self.unReadView);
  450. make.size.mas_equalTo(self.unReadView.unReadLabel);
  451. }];
  452. self.unReadView.layer.cornerRadius = kScale375(7);
  453. [self.unReadView.layer masksToBounds];
  454. [self.notDisturbRedDot mas_remakeConstraints:^(MASConstraintMaker *make) {
  455. make.trailing.mas_equalTo(self.headImageView.mas_trailing).mas_offset(3);
  456. make.top.mas_equalTo(self.headImageView.mas_top).mas_offset(1);
  457. make.width.height.mas_equalTo(TConversationCell_Margin_Disturb_Dot);
  458. }];
  459. MASAttachKeys(self.notDisturbRedDot);
  460. [self.notDisturbView mas_remakeConstraints:^(MASConstraintMaker *make) {
  461. make.width.height.mas_equalTo(TConversationCell_Margin_Disturb);
  462. make.trailing.mas_equalTo(self.timeLabel.mas_trailing);
  463. make.bottom.mas_equalTo(self.contentView.mas_bottom).mas_offset(-15);
  464. }];
  465. [self.onlineStatusIcon mas_remakeConstraints:^(MASConstraintMaker *make) {
  466. make.width.height.mas_equalTo(kScale375(15));
  467. make.leading.mas_equalTo(self.headImageView.mas_trailing).mas_offset(-kScale375(15));
  468. make.bottom.mas_equalTo(self.headImageView.mas_bottom).mas_offset(-kScale375(1));
  469. }];
  470. self.onlineStatusIcon.layer.cornerRadius = 0.5 * kScale375(15);
  471. }
  472. [self.nameTagLab mas_remakeConstraints:^(MASConstraintMaker *make) {
  473. make.leading.equalTo(self.titleLabel.mas_trailing).mas_offset(3);
  474. make.centerY.equalTo(self.titleLabel.mas_centerY);
  475. make.height.mas_equalTo(20);
  476. make.width.mas_greaterThanOrEqualTo(100);
  477. make.trailing.mas_lessThanOrEqualTo(self.timeLabel.mas_trailing).mas_offset(- 2*TConversationCell_Margin_Text);
  478. }];
  479. }
  480. - (void)layoutSubviews {
  481. [super layoutSubviews];
  482. }
  483. - (MOHeadNormalView *)headBgView{
  484. if(!_headBgView){
  485. _headBgView = [[MOHeadNormalView alloc] init];
  486. }
  487. return _headBgView;
  488. }
  489. - (YYLabel *)nameTagLab{
  490. if(!_nameTagLab){
  491. _nameTagLab = [[YYLabel alloc] init];
  492. _nameTagLab.textAlignment = NSTextAlignmentLeft;
  493. }
  494. return _nameTagLab;
  495. }
  496. - (UIView *)lineView{
  497. if(!_lineView){
  498. _lineView = [[UIView alloc] init];
  499. _lineView.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#E1E2E8");
  500. }
  501. return _lineView;
  502. }
  503. - (CGFloat)getWidthWithString:(NSString *)string font:(UIFont *)font
  504. {
  505. NSDictionary *attrs = @{NSFontAttributeName:font};
  506. CGFloat width = [string boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size.width;
  507. return width;
  508. }
  509. + (NSString *)convertDateTwoToStr:(NSDate *)date {
  510. if (!date) {
  511. return nil;
  512. }
  513. if ([date isEqualToDate:[NSDate distantPast]]) {
  514. return @"";
  515. }
  516. static NSDateFormatter *dateFmt = nil;
  517. if (dateFmt == nil) {
  518. dateFmt = [[NSDateFormatter alloc] init];
  519. }
  520. NSCalendar *calendar = [NSCalendar currentCalendar];
  521. NSDate *now = [NSDate date];
  522. NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitWeekOfYear | NSCalendarUnitMonth | NSCalendarUnitYear
  523. fromDate:date
  524. toDate:now
  525. options:0];
  526. // 同一天,显示时间
  527. if (components.year == 0 && components.month == 0 && components.day == 0) {
  528. dateFmt.dateFormat = @"HH:mm";
  529. return [dateFmt stringFromDate:date];
  530. }
  531. // 一天内,显示"一天前"
  532. else if (components.year == 0 && components.month == 0 && components.day <= 1) {
  533. return NSLocalizedString(@"mimo_chat_time_day_1", nil);
  534. }
  535. // 一周内,显示"一周前"
  536. else if (components.year == 0 && components.month == 0 && components.day <= 7) {
  537. return NSLocalizedString(@"mimo_chat_time_week_1", nil);
  538. }
  539. // 一月内,显示"一月前"
  540. else{
  541. return NSLocalizedString(@"mimo_chat_time_month_1", nil);
  542. }
  543. }
  544. @end