EaseMessageCell.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. /************************************************************
  2. * * Hyphenate CONFIDENTIAL
  3. * __________________
  4. * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
  5. *
  6. * NOTICE: All information contained herein is, and remains
  7. * the property of Hyphenate Inc.
  8. * Dissemination of this information or reproduction of this material
  9. * is strictly forbidden unless prior written permission is obtained
  10. * from Hyphenate Inc.
  11. */
  12. #import "EaseMessageCell.h"
  13. #import "EaseBubbleView+Text.h"
  14. #import "EaseBubbleView+Image.h"
  15. #import "EaseBubbleView+Location.h"
  16. #import "EaseBubbleView+Voice.h"
  17. #import "EaseBubbleView+Video.h"
  18. #import "EaseBubbleView+File.h"
  19. #import "UIImageView+EMWebCache.h"
  20. #import "EaseEmotionEscape.h"
  21. #import "EaseLocalDefine.h"
  22. CGFloat const EaseMessageCellPadding = 10;
  23. NSString *const EaseMessageCellIdentifierRecvText = @"EaseMessageCellRecvText";
  24. NSString *const EaseMessageCellIdentifierRecvLocation = @"EaseMessageCellRecvLocation";
  25. NSString *const EaseMessageCellIdentifierRecvVoice = @"EaseMessageCellRecvVoice";
  26. NSString *const EaseMessageCellIdentifierRecvVideo = @"EaseMessageCellRecvVideo";
  27. NSString *const EaseMessageCellIdentifierRecvImage = @"EaseMessageCellRecvImage";
  28. NSString *const EaseMessageCellIdentifierRecvFile = @"EaseMessageCellRecvFile";
  29. NSString *const EaseMessageCellIdentifierSendText = @"EaseMessageCellSendText";
  30. NSString *const EaseMessageCellIdentifierSendLocation = @"EaseMessageCellSendLocation";
  31. NSString *const EaseMessageCellIdentifierSendVoice = @"EaseMessageCellSendVoice";
  32. NSString *const EaseMessageCellIdentifierSendVideo = @"EaseMessageCellSendVideo";
  33. NSString *const EaseMessageCellIdentifierSendImage = @"EaseMessageCellSendImage";
  34. NSString *const EaseMessageCellIdentifierSendFile = @"EaseMessageCellSendFile";
  35. @interface EaseMessageCell()
  36. {
  37. EMMessageBodyType _messageType;
  38. }
  39. @property (nonatomic) NSLayoutConstraint *statusWidthConstraint;
  40. @property (nonatomic) NSLayoutConstraint *activtiyWidthConstraint;
  41. @property (nonatomic) NSLayoutConstraint *hasReadWidthConstraint;
  42. @property (nonatomic) NSLayoutConstraint *bubbleMaxWidthConstraint;
  43. @end
  44. @implementation EaseMessageCell
  45. @synthesize statusButton = _statusButton;
  46. @synthesize bubbleView = _bubbleView;
  47. @synthesize hasRead = _hasRead;
  48. @synthesize activity = _activity;
  49. + (void)initialize
  50. {
  51. // UIAppearance Proxy Defaults
  52. EaseMessageCell *cell = [self appearance];
  53. cell.statusSize = 20;
  54. cell.activitySize = 20;
  55. cell.bubbleMaxWidth = 200;
  56. cell.leftBubbleMargin = UIEdgeInsetsMake(8, 15, 8, 10);
  57. cell.rightBubbleMargin = UIEdgeInsetsMake(8, 10, 8, 15);
  58. cell.bubbleMargin = UIEdgeInsetsMake(8, 0, 8, 0);
  59. cell.messageTextFont = [UIFont systemFontOfSize:15];
  60. cell.messageTextColor = [UIColor blackColor];
  61. cell.messageLocationFont = [UIFont systemFontOfSize:10];
  62. cell.messageLocationColor = [UIColor whiteColor];
  63. cell.messageVoiceDurationColor = [UIColor grayColor];
  64. cell.messageVoiceDurationFont = [UIFont systemFontOfSize:12];
  65. cell.voiceCellWidth = 75.f;
  66. cell.messageFileNameColor = [UIColor blackColor];
  67. cell.messageFileNameFont = [UIFont systemFontOfSize:13];
  68. cell.messageFileSizeColor = [UIColor grayColor];
  69. cell.messageFileSizeFont = [UIFont systemFontOfSize:11];
  70. }
  71. - (instancetype)initWithStyle:(UITableViewCellStyle)style
  72. reuseIdentifier:(NSString *)reuseIdentifier
  73. model:(id<IMessageModel>)model
  74. {
  75. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  76. if (self) {
  77. self.accessibilityIdentifier = @"table_cell";
  78. _messageType = model.bodyType;
  79. [self _setupSubviewsWithType:_messageType
  80. isSender:model.isSender
  81. model:model];
  82. }
  83. return self;
  84. }
  85. - (BOOL)canBecomeFirstResponder
  86. {
  87. return YES;
  88. }
  89. #pragma mark - setup subviews
  90. /*!
  91. @method
  92. @brief 加载子视图
  93. @discussion
  94. @param messageType 消息体类型
  95. @param isSender 登录用户是否为发送方
  96. @param model 消息对象model
  97. @result
  98. */
  99. - (void)_setupSubviewsWithType:(EMMessageBodyType)messageType
  100. isSender:(BOOL)isSender
  101. model:(id<IMessageModel>)model
  102. {
  103. _statusButton = [[UIButton alloc] init];
  104. _statusButton.accessibilityIdentifier = @"status";
  105. _statusButton.translatesAutoresizingMaskIntoConstraints = NO;
  106. _statusButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  107. [_statusButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/messageSendFail"] forState:UIControlStateNormal];
  108. [_statusButton addTarget:self action:@selector(statusAction) forControlEvents:UIControlEventTouchUpInside];
  109. _statusButton.hidden = YES;
  110. [self.contentView addSubview:_statusButton];
  111. _bubbleView = [[EaseBubbleView alloc] initWithMargin:isSender?_rightBubbleMargin:_leftBubbleMargin isSender:isSender];
  112. _bubbleView.translatesAutoresizingMaskIntoConstraints = NO;
  113. _bubbleView.backgroundColor = [UIColor clearColor];
  114. [self.contentView addSubview:_bubbleView];
  115. _avatarView = [[UIImageView alloc] init];
  116. _avatarView.translatesAutoresizingMaskIntoConstraints = NO;
  117. _avatarView.backgroundColor = [UIColor clearColor];
  118. _avatarView.clipsToBounds = YES;
  119. _avatarView.userInteractionEnabled = YES;
  120. [self.contentView addSubview:_avatarView];
  121. _hasRead = [[UILabel alloc] init];
  122. _hasRead.accessibilityIdentifier = @"has_read";
  123. _hasRead.translatesAutoresizingMaskIntoConstraints = NO;
  124. // _hasRead.text = NSEaseLocalizedString(@"hasRead", @"Read");
  125. _hasRead.textAlignment = NSTextAlignmentCenter;
  126. _hasRead.font = [UIFont systemFontOfSize:12];
  127. _hasRead.hidden = YES;
  128. [_hasRead sizeToFit];
  129. [self.contentView addSubview:_hasRead];
  130. _activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
  131. _activity.accessibilityIdentifier = @"sending";
  132. _activity.translatesAutoresizingMaskIntoConstraints = NO;
  133. _activity.backgroundColor = [UIColor clearColor];
  134. _activity.hidden = YES;
  135. [self.contentView addSubview:_activity];
  136. if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:model]) {
  137. [self setCustomBubbleView:model];
  138. } else {
  139. switch (messageType) {
  140. case EMMessageBodyTypeText:
  141. {
  142. [_bubbleView setupTextBubbleView];
  143. _bubbleView.textLabel.font = _messageTextFont;
  144. _bubbleView.textLabel.textColor = _messageTextColor;
  145. }
  146. break;
  147. case EMMessageBodyTypeImage:
  148. {
  149. [_bubbleView setupImageBubbleView];
  150. _bubbleView.imageView.image = [UIImage imageNamed:@"EaseUIResource.bundle/imageDownloadFail"];
  151. }
  152. break;
  153. case EMMessageBodyTypeLocation:
  154. {
  155. [_bubbleView setupLocationBubbleView];
  156. _bubbleView.locationImageView.image = [[UIImage imageNamed:@"EaseUIResource.bundle/chat_location_preview"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
  157. _bubbleView.locationLabel.font = _messageLocationFont;
  158. _bubbleView.locationLabel.textColor = _messageLocationColor;
  159. }
  160. break;
  161. case EMMessageBodyTypeVoice:
  162. {
  163. [_bubbleView setupVoiceBubbleView];
  164. _bubbleView.voiceDurationLabel.textColor = _messageVoiceDurationColor;
  165. _bubbleView.voiceDurationLabel.font = _messageVoiceDurationFont;
  166. }
  167. break;
  168. case EMMessageBodyTypeVideo:
  169. {
  170. [_bubbleView setupVideoBubbleView];
  171. _bubbleView.videoTagView.image = [UIImage imageNamed:@"EaseUIResource.bundle/messageVideo"];
  172. }
  173. break;
  174. case EMMessageBodyTypeFile:
  175. {
  176. [_bubbleView setupFileBubbleView];
  177. _bubbleView.fileNameLabel.font = _messageFileNameFont;
  178. _bubbleView.fileNameLabel.textColor = _messageFileNameColor;
  179. _bubbleView.fileSizeLabel.font = _messageFileSizeFont;
  180. }
  181. break;
  182. default:
  183. break;
  184. }
  185. }
  186. [self _setupConstraints];
  187. UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(bubbleViewTapAction:)];
  188. [_bubbleView addGestureRecognizer:tapRecognizer];
  189. UITapGestureRecognizer *tapRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(avatarViewTapAction:)];
  190. [_avatarView addGestureRecognizer:tapRecognizer2];
  191. }
  192. #pragma mark - Setup Constraints
  193. /*!
  194. @method
  195. @brief 设置控件约束
  196. @discussion 设置气泡宽度、发送状态、已读提示的约束
  197. @result
  198. */
  199. - (void)_setupConstraints
  200. {
  201. //bubble view
  202. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.bubbleView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-EaseMessageCellPadding]];
  203. self.bubbleMaxWidthConstraint = [NSLayoutConstraint constraintWithItem:self.bubbleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:self.bubbleMaxWidth];
  204. [self addConstraint:self.bubbleMaxWidthConstraint];
  205. // self.bubbleMaxWidthConstraint.active = YES;
  206. //status button
  207. self.statusWidthConstraint = [NSLayoutConstraint constraintWithItem:self.statusButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:self.statusSize];
  208. [self addConstraint:self.statusWidthConstraint];
  209. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.statusButton attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.statusButton attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
  210. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.statusButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
  211. //activtiy
  212. self.activtiyWidthConstraint = [NSLayoutConstraint constraintWithItem:self.activity attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:self.activitySize];
  213. [self addConstraint:self.activtiyWidthConstraint];
  214. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activity attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.activity attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
  215. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activity attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
  216. [self _updateHasReadWidthConstraint];
  217. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.hasRead attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
  218. [self addConstraint:[NSLayoutConstraint constraintWithItem:self.hasRead attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.statusButton attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
  219. // [self addConstraint:[NSLayoutConstraint constraintWithItem:self.hasRead attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.activity attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
  220. }
  221. #pragma mark - Update Constraint
  222. /*!
  223. @method
  224. @brief 修改已读Label的宽度约束
  225. @discussion
  226. @result
  227. */
  228. - (void)_updateHasReadWidthConstraint
  229. {
  230. if (_hasRead) {
  231. [self removeConstraint:self.hasReadWidthConstraint];
  232. self.hasReadWidthConstraint = [NSLayoutConstraint constraintWithItem:_hasRead attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:40];
  233. [self addConstraint:self.hasReadWidthConstraint];
  234. }
  235. }
  236. /*!
  237. @method
  238. @brief 修改发送状态按钮的宽度约束,主要是发送失败的状态
  239. @discussion
  240. @result
  241. */
  242. - (void)_updateStatusButtonWidthConstraint
  243. {
  244. if (_statusButton) {
  245. [self removeConstraint:self.statusWidthConstraint];
  246. self.statusWidthConstraint = [NSLayoutConstraint constraintWithItem:self.statusButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:self.statusSize];
  247. [self addConstraint:self.statusWidthConstraint];
  248. }
  249. }
  250. /*!
  251. @method
  252. @brief 修改发送进度的宽度约束
  253. @discussion
  254. @result
  255. */
  256. - (void)_updateActivityWidthConstraint
  257. {
  258. if (_activity) {
  259. [self removeConstraint:self.activtiyWidthConstraint];
  260. self.statusWidthConstraint = [NSLayoutConstraint constraintWithItem:self.activity attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:self.activitySize];
  261. [self addConstraint:self.activtiyWidthConstraint];
  262. }
  263. }
  264. /*!
  265. @method
  266. @brief 修改气泡的宽度约束
  267. @discussion
  268. @result
  269. */
  270. - (void)_updateBubbleMaxWidthConstraint
  271. {
  272. [self removeConstraint:self.bubbleMaxWidthConstraint];
  273. // self.bubbleMaxWidthConstraint.active = NO;
  274. //气泡宽度小于等于bubbleMaxWidth
  275. self.bubbleMaxWidthConstraint = [NSLayoutConstraint constraintWithItem:self.bubbleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:self.bubbleMaxWidth];
  276. [self addConstraint:self.bubbleMaxWidthConstraint];
  277. // self.bubbleMaxWidthConstraint.active = YES;
  278. }
  279. #pragma mark - setter
  280. - (void)setModel:(id<IMessageModel>)model
  281. {
  282. _model = model;
  283. if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:model]) {
  284. [self setCustomModel:model];
  285. } else {
  286. switch (model.bodyType) {
  287. case EMMessageBodyTypeText:
  288. {
  289. _bubbleView.textLabel.attributedText = [[EaseEmotionEscape sharedInstance] attStringFromTextForChatting:model.text textFont:self.messageTextFont];
  290. }
  291. break;
  292. case EMMessageBodyTypeImage:
  293. {
  294. UIImage *image = _model.thumbnailImage;
  295. if (!image) {
  296. image = _model.image;
  297. if (!image) {
  298. [_bubbleView.imageView sd_setImageWithURL:[NSURL URLWithString:_model.fileURLPath] placeholderImage:[UIImage imageNamed:_model.failImageName]];
  299. } else {
  300. _bubbleView.imageView.image = image;
  301. }
  302. } else {
  303. _bubbleView.imageView.image = image;
  304. }
  305. }
  306. break;
  307. case EMMessageBodyTypeLocation:
  308. {
  309. _bubbleView.locationLabel.text = _model.address;
  310. }
  311. break;
  312. case EMMessageBodyTypeVoice:
  313. {
  314. if (_bubbleView.voiceImageView) {
  315. if ([self.sendMessageVoiceAnimationImages count] > 0 && [self.recvMessageVoiceAnimationImages count] > 0) {
  316. self.bubbleView.voiceImageView.image = self.model.isSender ?[self.sendMessageVoiceAnimationImages objectAtIndex:0] : [self.recvMessageVoiceAnimationImages objectAtIndex:0];
  317. _bubbleView.voiceImageView.animationImages = self.model.isSender ? self.sendMessageVoiceAnimationImages:self.recvMessageVoiceAnimationImages;
  318. } else {
  319. self.bubbleView.voiceImageView.image = self.model.isSender ?[UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_full"]: [UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing_full"];
  320. }
  321. }
  322. if (!self.model.isSender) {
  323. if (self.model.isMediaPlayed){
  324. _bubbleView.isReadView.hidden = YES;
  325. } else {
  326. _bubbleView.isReadView.hidden = NO;
  327. }
  328. }
  329. if (_model.isMediaPlaying) {
  330. [_bubbleView.voiceImageView startAnimating];
  331. }
  332. else{
  333. [_bubbleView.voiceImageView stopAnimating];
  334. }
  335. _bubbleView.voiceDurationLabel.text = [NSString stringWithFormat:@"%d''",(int)_model.mediaDuration];
  336. }
  337. break;
  338. case EMMessageBodyTypeVideo:
  339. {
  340. UIImage *image = _model.isSender ? _model.image : _model.thumbnailImage;
  341. if (!image) {
  342. image = _model.image;
  343. if (!image) {
  344. [_bubbleView.videoImageView sd_setImageWithURL:[NSURL URLWithString:_model.fileURLPath] placeholderImage:[UIImage imageNamed:_model.failImageName]];
  345. } else {
  346. _bubbleView.videoImageView.image = image;
  347. }
  348. } else {
  349. _bubbleView.videoImageView.image = image;
  350. }
  351. }
  352. break;
  353. case EMMessageBodyTypeFile:
  354. {
  355. _bubbleView.fileIconView.image = [UIImage imageNamed:_model.fileIconName];
  356. _bubbleView.fileNameLabel.text = _model.fileName;
  357. _bubbleView.fileSizeLabel.text = _model.fileSizeDes;
  358. }
  359. break;
  360. default:
  361. break;
  362. }
  363. }
  364. }
  365. - (void)setStatusSize:(CGFloat)statusSize
  366. {
  367. _statusSize = statusSize;
  368. [self _updateStatusButtonWidthConstraint];
  369. }
  370. - (void)setActivitySize:(CGFloat)activitySize
  371. {
  372. _activitySize = activitySize;
  373. [self _updateActivityWidthConstraint];
  374. }
  375. - (void)setSendBubbleBackgroundImage:(UIImage *)sendBubbleBackgroundImage
  376. {
  377. _sendBubbleBackgroundImage = sendBubbleBackgroundImage;
  378. }
  379. - (void)setRecvBubbleBackgroundImage:(UIImage *)recvBubbleBackgroundImage
  380. {
  381. _recvBubbleBackgroundImage = recvBubbleBackgroundImage;
  382. }
  383. - (void)setBubbleMaxWidth:(CGFloat)bubbleMaxWidth
  384. {
  385. _bubbleMaxWidth = bubbleMaxWidth;
  386. [self _updateBubbleMaxWidthConstraint];
  387. }
  388. - (void)setRightBubbleMargin:(UIEdgeInsets)rightBubbleMargin
  389. {
  390. _rightBubbleMargin = rightBubbleMargin;
  391. }
  392. - (void)setLeftBubbleMargin:(UIEdgeInsets)leftBubbleMargin
  393. {
  394. _leftBubbleMargin = leftBubbleMargin;
  395. }
  396. - (void)setBubbleMargin:(UIEdgeInsets)bubbleMargin
  397. {
  398. _bubbleMargin = bubbleMargin;
  399. _bubbleMargin = self.model.isSender ? _rightBubbleMargin:_leftBubbleMargin;
  400. if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:_model]) {
  401. [self updateCustomBubbleViewMargin:_bubbleMargin model:_model];
  402. } else {
  403. if (_bubbleView) {
  404. switch (_messageType) {
  405. case EMMessageBodyTypeText:
  406. {
  407. [_bubbleView updateTextMargin:_bubbleMargin];
  408. }
  409. break;
  410. case EMMessageBodyTypeImage:
  411. {
  412. [_bubbleView updateImageMargin:_bubbleMargin];
  413. }
  414. break;
  415. case EMMessageBodyTypeLocation:
  416. {
  417. [_bubbleView updateLocationMargin:_bubbleMargin];
  418. }
  419. break;
  420. case EMMessageBodyTypeVoice:
  421. {
  422. [_bubbleView updateVoiceMargin:_bubbleMargin];
  423. }
  424. break;
  425. case EMMessageBodyTypeVideo:
  426. {
  427. [_bubbleView updateVideoMargin:_bubbleMargin];
  428. }
  429. break;
  430. case EMMessageBodyTypeFile:
  431. {
  432. [_bubbleView updateFileMargin:_bubbleMargin];
  433. }
  434. break;
  435. default:
  436. break;
  437. }
  438. }
  439. }
  440. }
  441. - (void)setMessageTextFont:(UIFont *)messageTextFont
  442. {
  443. _messageTextFont = messageTextFont;
  444. if (_bubbleView.textLabel) {
  445. _bubbleView.textLabel.font = messageTextFont;
  446. }
  447. }
  448. - (void)setMessageTextColor:(UIColor *)messageTextColor
  449. {
  450. _messageTextColor = messageTextColor;
  451. if (_bubbleView.textLabel) {
  452. _bubbleView.textLabel.textColor = _messageTextColor;
  453. }
  454. }
  455. - (void)setMessageLocationColor:(UIColor *)messageLocationColor
  456. {
  457. _messageLocationColor = messageLocationColor;
  458. if (_bubbleView.locationLabel) {
  459. _bubbleView.locationLabel.textColor = _messageLocationColor;
  460. }
  461. }
  462. - (void)setMessageLocationFont:(UIFont *)messageLocationFont
  463. {
  464. _messageLocationFont = messageLocationFont;
  465. if (_bubbleView.locationLabel) {
  466. _bubbleView.locationLabel.font = _messageLocationFont;
  467. }
  468. }
  469. - (void)setSendMessageVoiceAnimationImages:(NSArray *)sendMessageVoiceAnimationImages
  470. {
  471. _sendMessageVoiceAnimationImages = sendMessageVoiceAnimationImages;
  472. }
  473. - (void)setRecvMessageVoiceAnimationImages:(NSArray *)recvMessageVoiceAnimationImages
  474. {
  475. _recvMessageVoiceAnimationImages = recvMessageVoiceAnimationImages;
  476. }
  477. - (void)setMessageVoiceDurationColor:(UIColor *)messageVoiceDurationColor
  478. {
  479. _messageVoiceDurationColor = messageVoiceDurationColor;
  480. if (_bubbleView.voiceDurationLabel) {
  481. _bubbleView.voiceDurationLabel.textColor = _messageVoiceDurationColor;
  482. }
  483. }
  484. - (void)setMessageVoiceDurationFont:(UIFont *)messageVoiceDurationFont
  485. {
  486. _messageVoiceDurationFont = messageVoiceDurationFont;
  487. if (_bubbleView.voiceDurationLabel) {
  488. _bubbleView.voiceDurationLabel.font = _messageVoiceDurationFont;
  489. }
  490. }
  491. - (void)setMessageFileNameFont:(UIFont *)messageFileNameFont
  492. {
  493. _messageFileNameFont = messageFileNameFont;
  494. if (_bubbleView.fileNameLabel) {
  495. _bubbleView.fileNameLabel.font = _messageFileNameFont;
  496. }
  497. }
  498. - (void)setMessageFileNameColor:(UIColor *)messageFileNameColor
  499. {
  500. _messageFileNameColor = messageFileNameColor;
  501. if (_bubbleView.fileNameLabel) {
  502. _bubbleView.fileNameLabel.textColor = _messageFileNameColor;
  503. }
  504. }
  505. - (void)setMessageFileSizeFont:(UIFont *)messageFileSizeFont
  506. {
  507. _messageFileSizeFont = messageFileSizeFont;
  508. if (_bubbleView.fileSizeLabel) {
  509. _bubbleView.fileSizeLabel.font = _messageFileSizeFont;
  510. }
  511. }
  512. - (void)setMessageFileSizeColor:(UIColor *)messageFileSizeColor
  513. {
  514. _messageFileSizeColor = messageFileSizeColor;
  515. if (_bubbleView.fileSizeLabel) {
  516. _bubbleView.fileSizeLabel.textColor = _messageFileSizeColor;
  517. }
  518. }
  519. #pragma mark - action
  520. /*!
  521. @method
  522. @brief 气泡的点击手势事件
  523. @discussion
  524. @result
  525. */
  526. - (void)bubbleViewTapAction:(UITapGestureRecognizer *)tapRecognizer
  527. {
  528. if (tapRecognizer.state == UIGestureRecognizerStateEnded) {
  529. if (!_delegate) {
  530. return;
  531. }
  532. if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:_model]) {
  533. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  534. [_delegate messageCellSelected:_model];
  535. return;
  536. }
  537. }
  538. switch (_model.bodyType) {
  539. case EMMessageBodyTypeImage:
  540. {
  541. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  542. [_delegate messageCellSelected:_model];
  543. }
  544. }
  545. break;
  546. case EMMessageBodyTypeLocation:
  547. {
  548. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  549. [_delegate messageCellSelected:_model];
  550. }
  551. }
  552. break;
  553. case EMMessageBodyTypeVoice:
  554. {
  555. // _model.isMediaPlaying = !_model.isMediaPlaying;
  556. // if (_model.isMediaPlaying) {
  557. // [_bubbleView.voiceImageView startAnimating];
  558. // }
  559. // else{
  560. // [_bubbleView.voiceImageView stopAnimating];
  561. // }
  562. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  563. [_delegate messageCellSelected:_model];
  564. }
  565. }
  566. break;
  567. case EMMessageBodyTypeVideo:
  568. {
  569. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  570. [_delegate messageCellSelected:_model];
  571. }
  572. }
  573. break;
  574. case EMMessageBodyTypeFile:
  575. {
  576. if ([_delegate respondsToSelector:@selector(messageCellSelected:)]) {
  577. [_delegate messageCellSelected:_model];
  578. }
  579. }
  580. break;
  581. default:
  582. break;
  583. }
  584. }
  585. }
  586. /*!
  587. @method
  588. @brief 头像的点击事件
  589. @discussion
  590. @result
  591. */
  592. - (void)avatarViewTapAction:(UITapGestureRecognizer *)tapRecognizer
  593. {
  594. if ([_delegate respondsToSelector:@selector(avatarViewSelcted:)]) {
  595. [_delegate avatarViewSelcted:_model];
  596. }
  597. }
  598. /*!
  599. @method
  600. @brief 发送失败按钮的点击事件,进行消息重发
  601. @discussion
  602. @result
  603. */
  604. - (void)statusAction
  605. {
  606. if ([_delegate respondsToSelector:@selector(statusButtonSelcted:withMessageCell:)]) {
  607. [_delegate statusButtonSelcted:_model withMessageCell:self];
  608. }
  609. }
  610. #pragma mark - IModelCell
  611. /*
  612. - (BOOL)isCustomBubbleView:(id<IMessageModel>)model
  613. {
  614. return NO;
  615. }
  616. - (void)setCustomModel:(id<IMessageModel>)model
  617. {
  618. }
  619. - (void)setCustomBubbleView:(id<IMessageModel>)model
  620. {
  621. }
  622. - (void)updateCustomBubbleViewMargin:(UIEdgeInsets)bubbleMargin model:(id<IMessageModel>)model
  623. {
  624. }*/
  625. #pragma mark - public
  626. /*!
  627. @method
  628. @brief 获取cell的重用标识
  629. @discussion
  630. @param model 消息对象model
  631. @result 返回cell的重用标识
  632. */
  633. + (NSString *)cellIdentifierWithModel:(id<IMessageModel>)model
  634. {
  635. NSString *cellIdentifier = nil;
  636. if (model.isSender) {
  637. switch (model.bodyType) {
  638. case EMMessageBodyTypeText:
  639. cellIdentifier = EaseMessageCellIdentifierSendText;
  640. break;
  641. case EMMessageBodyTypeImage:
  642. cellIdentifier = EaseMessageCellIdentifierSendImage;
  643. break;
  644. case EMMessageBodyTypeVideo:
  645. cellIdentifier = EaseMessageCellIdentifierSendVideo;
  646. break;
  647. case EMMessageBodyTypeLocation:
  648. cellIdentifier = EaseMessageCellIdentifierSendLocation;
  649. break;
  650. case EMMessageBodyTypeVoice:
  651. cellIdentifier = EaseMessageCellIdentifierSendVoice;
  652. break;
  653. case EMMessageBodyTypeFile:
  654. cellIdentifier = EaseMessageCellIdentifierSendFile;
  655. break;
  656. default:
  657. break;
  658. }
  659. }
  660. else{
  661. switch (model.bodyType) {
  662. case EMMessageBodyTypeText:
  663. cellIdentifier = EaseMessageCellIdentifierRecvText;
  664. break;
  665. case EMMessageBodyTypeImage:
  666. cellIdentifier = EaseMessageCellIdentifierRecvImage;
  667. break;
  668. case EMMessageBodyTypeVideo:
  669. cellIdentifier = EaseMessageCellIdentifierRecvVideo;
  670. break;
  671. case EMMessageBodyTypeLocation:
  672. cellIdentifier = EaseMessageCellIdentifierRecvLocation;
  673. break;
  674. case EMMessageBodyTypeVoice:
  675. cellIdentifier = EaseMessageCellIdentifierRecvVoice;
  676. break;
  677. case EMMessageBodyTypeFile:
  678. cellIdentifier = EaseMessageCellIdentifierRecvFile;
  679. break;
  680. default:
  681. break;
  682. }
  683. }
  684. return cellIdentifier;
  685. }
  686. /*!
  687. @method
  688. @brief 根据消息的内容,获取当前cell的高度
  689. @discussion
  690. @param model 消息对象model
  691. @result 返回cell高度
  692. */
  693. + (CGFloat)cellHeightWithModel:(id<IMessageModel>)model
  694. {
  695. if (model.cellHeight > 0) {
  696. return model.cellHeight;
  697. }
  698. EaseMessageCell *cell = [self appearance];
  699. CGFloat bubbleMaxWidth = cell.bubbleMaxWidth;
  700. if ([UIDevice currentDevice].systemVersion.floatValue == 7.0) {
  701. bubbleMaxWidth = 200;
  702. }
  703. bubbleMaxWidth -= (cell.leftBubbleMargin.left + cell.leftBubbleMargin.right + cell.rightBubbleMargin.left + cell.rightBubbleMargin.right)/2;
  704. CGFloat height = EaseMessageCellPadding + cell.bubbleMargin.top + cell.bubbleMargin.bottom;
  705. switch (model.bodyType) {
  706. case EMMessageBodyTypeText:
  707. {
  708. NSAttributedString *text = [[EaseEmotionEscape sharedInstance] attStringFromTextForChatting:model.text textFont:cell.messageTextFont];
  709. CGRect rect = [text boundingRectWithSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
  710. height += (rect.size.height > 20 ? rect.size.height : 20) + 10;
  711. // NSString *text = model.text;
  712. // UIFont *textFont = cell.messageTextFont;
  713. // CGSize retSize;
  714. // if ([NSString instancesRespondToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
  715. // retSize = [text boundingRectWithSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:textFont} context:nil].size;
  716. // }else{
  717. // retSize = [text sizeWithFont:textFont constrainedToSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) lineBreakMode:NSLineBreakByCharWrapping];
  718. // }
  719. //
  720. //
  721. // height += (retSize.height > 20 ? retSize.height : 20) + 10;
  722. }
  723. break;
  724. case EMMessageBodyTypeImage:
  725. case EMMessageBodyTypeVideo:
  726. {
  727. CGSize retSize = model.thumbnailImageSize;
  728. if (retSize.width == 0 || retSize.height == 0) {
  729. retSize.width = kEMMessageImageSizeWidth;
  730. retSize.height = kEMMessageImageSizeHeight;
  731. }
  732. else if (retSize.width > retSize.height) {
  733. CGFloat height = kEMMessageImageSizeWidth / retSize.width * retSize.height;
  734. retSize.height = height;
  735. retSize.width = kEMMessageImageSizeWidth;
  736. }
  737. else {
  738. CGFloat width = kEMMessageImageSizeHeight / retSize.height * retSize.width;
  739. retSize.width = width;
  740. retSize.height = kEMMessageImageSizeHeight;
  741. }
  742. height += retSize.height;
  743. }
  744. break;
  745. case EMMessageBodyTypeLocation:
  746. {
  747. height += kEMMessageLocationHeight;
  748. }
  749. break;
  750. case EMMessageBodyTypeVoice:
  751. {
  752. height += kEMMessageVoiceHeight;
  753. }
  754. break;
  755. case EMMessageBodyTypeFile:
  756. {
  757. NSString *text = model.fileName;
  758. UIFont *font = cell.messageFileNameFont;
  759. CGRect nameRect;
  760. if ([NSString instancesRespondToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
  761. nameRect = [text boundingRectWithSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil];
  762. } else {
  763. nameRect.size = [text sizeWithFont:font constrainedToSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) lineBreakMode:NSLineBreakByCharWrapping];
  764. }
  765. height += (nameRect.size.height > 20 ? nameRect.size.height : 20);
  766. text = model.fileSizeDes;
  767. font = cell.messageFileSizeFont;
  768. CGRect sizeRect;
  769. if ([NSString instancesRespondToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
  770. sizeRect = [text boundingRectWithSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil];
  771. } else {
  772. sizeRect.size = [text sizeWithFont:font constrainedToSize:CGSizeMake(bubbleMaxWidth, CGFLOAT_MAX) lineBreakMode:NSLineBreakByCharWrapping];
  773. }
  774. height += (sizeRect.size.height > 15 ? sizeRect.size.height : 15);
  775. }
  776. break;
  777. default:
  778. break;
  779. }
  780. height += EaseMessageCellPadding;
  781. model.cellHeight = height;
  782. return height;
  783. }
  784. @end