// // XHMessageBubbleView.m // MessageDisplayExample // // Created by HUAJIE-1 on 14-4-24. // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved. // #import "XHMessageBubbleView.h" #import "XHMessageBubbleHelper.h" #import "XHConfigurationHelper.h" #import "myHelper.h" #define kXHHaveBubbleMargin 5.0f // 文本、视频、表情气泡上下边的间隙 #define kXHHaveBubbleVoiceMargin 5 // 语音气泡上下边的间隙 #define kXHHaveBubblePhotoMargin 6.5f // 图片、地理位置气泡上下边的间隙 #define kXHVoiceMargin 20.0f // 播放语音时的动画控件距离头像的间隙 #define kXHArrowMarginWidth 5.2f // 箭头宽度 #define kXHTopAndBottomBubbleMargin 10.0f // 文本在气泡内部的上下间隙 #define kXHLeftTextHorizontalBubblePadding 8.0f // 文本的水平间隙 #define kXHRightTextHorizontalBubblePadding 8.0f // 文本的水平间隙 #define kXHUnReadDotSize 7.0f // 语音未读的红点大小 #define kXHNoneBubblePhotoMargin (kXHHaveBubbleMargin - kXHBubblePhotoMargin) // 在没有气泡的时候,也就是在图片、视频、地理位置的时候,图片内部做了Margin,所以需要减去内部的Margin @interface XHMessageBubbleView () @property (nonatomic, weak, readwrite) SETextView *displayTextView; @property (nonatomic, weak, readwrite) UIImageView *bubbleImageView; @property (nonatomic, weak, readwrite) FLAnimatedImageView *emotionImageView; @property (nonatomic, weak, readwrite) UIImageView *animationVoiceImageView; @property (nonatomic, weak, readwrite) UIImageView *voiceUnreadDotImageView; @property (nonatomic, weak, readwrite) UILabel *voiceDurationLabel; @property (nonatomic, weak, readwrite) XHBubblePhotoImageView *bubblePhotoImageView; @property (nonatomic, weak, readwrite) UIImageView *videoPlayImageView; @property (nonatomic, weak, readwrite) UILabel *geolocationsLabel; @property (nonatomic, strong, readwrite) XHMessage * message; @end @implementation XHMessageBubbleView #pragma mark - Bubble view // 获取文本的实际大小 + (CGFloat)neededWidthForText:(NSString *)text { UIFont *systemFont = [[XHMessageBubbleView appearance] font]; CGSize textSize = CGSizeMake(CGFLOAT_MAX, 20); // rough accessory size CGSize sizeWithFont = [text sizeWithFont:systemFont constrainedToSize:textSize lineBreakMode:NSLineBreakByWordWrapping]; #if defined(__LP64__) && __LP64__ return ceil(sizeWithFont.width); #else return ceilf(sizeWithFont.width); #endif } // 计算文本实际的大小 + (CGSize)neededSizeForText:(NSString *)text { // 实际处理文本的时候 // 文本只有一行的时候,宽度可能出现很小到最大的情况,所以需要计算一行文字需要的宽度 CGFloat maxWidth = CGRectGetWidth([[UIScreen mainScreen] bounds]) * (kIsiPad ? 0.8 : (kIs_iPhone_6 ? 0.6 : (kIs_iPhone_6P ? 0.62 : 0.55))); CGFloat dyWidth = [XHMessageBubbleView neededWidthForText:text]; if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) { dyWidth += 5; } CGSize textSize = [SETextView frameRectWithAttributtedString:[[XHMessageBubbleHelper sharedMessageBubbleHelper] bubbleAttributtedStringWithText:text] constraintSize:CGSizeMake(maxWidth, MAXFLOAT) lineSpacing:kXHTextLineSpacing font:[[XHMessageBubbleView appearance] font]].size; return CGSizeMake((dyWidth > textSize.width ? textSize.width : dyWidth), textSize.height); } // 计算图片实际大小 + (CGSize)neededSizeForPhoto:(UIImage *)photo { // 这里需要缩放后的size CGSize photoSize = CGSizeMake(140, 140); return photoSize; } // 计算语音实际大小 + (CGSize)neededSizeForVoicePath:(NSString *)voicePath voiceDuration:(NSString *)voiceDuration { #warning Here we define the width of a speech over 15s in terms of the length of 15s //这里定义超过15s 语音的按照15s 的时长来计算宽度 if([voiceDuration floatValue] > 15){ voiceDuration = @"15"; } // 这里的100只是暂时固定,到时候会根据一个函数来计算 float gapDuration = (!voiceDuration || voiceDuration.length == 0 ? -1 : [voiceDuration floatValue] - 1.0f); CGSize voiceSize = CGSizeMake(100 + (gapDuration > 0 ? (120.0 / (kVoiceRecorderTotalTime - 1) * gapDuration) : 0), 42); return voiceSize; } // 计算Emotion的高度 + (CGSize)neededSizeForEmotion { return CGSizeMake(100, 100); } // 计算LocalPostion的高度 + (CGSize)neededSizeForLocalPostion { return CGSizeMake(140, 140); } // 判断问文字消息内容是否是中文 + (BOOL)IsChinese:(NSString *)str { for(int i=0; i< [str length];i++) { int a = [str characterAtIndex:i]; if( a > 0x4e00 && a < 0x9fff) { return YES; } } return NO; } // 计算Cell需要实际Message内容的大小 + (CGFloat)calculateCellHeightWithMessage:(XHMessage *)message { CGSize size = [XHMessageBubbleView getBubbleFrameWithMessage:message]; return size.height; } // 获取Cell需要的高度 + (CGSize)getBubbleFrameWithMessage:(XHMessage *)message { CGSize bubbleSize; switch (message.messageMediaType) { case XHBubbleMessageMediaTypeText: { CGFloat ise = [self IsChinese:message.content] ? 2 : 5; CGSize needTextSize = [XHMessageBubbleView neededSizeForText:message.content]; bubbleSize = CGSizeMake(needTextSize.width + kXHLeftTextHorizontalBubblePadding + kXHRightTextHorizontalBubblePadding , needTextSize.height + kXHHaveBubbleMargin * 2 + kXHTopAndBottomBubbleMargin * 2 + ise); //这里*4的原因是:气泡内部的文本也做了margin,而且margin的大小和气泡的margin一样大小,所以需要加上*2的间隙大小 break; } case XHBubbleMessageMediaTypeVoice: { // 这里的宽度是不定的,高度是固定的,根据需要根据语音长短来定制啦 CGSize needVoiceSize = [XHMessageBubbleView neededSizeForVoicePath:message.voicePath voiceDuration:[NSString stringWithFormat:@"%d",[message.duration intValue]]]; bubbleSize = CGSizeMake(needVoiceSize.width, needVoiceSize.height + kXHHaveBubbleVoiceMargin * 2); break; } case XHBubbleMessageMediaTypeEmotion: { // 是否固定大小呢? CGSize emotionSize = [XHMessageBubbleView neededSizeForEmotion]; bubbleSize = CGSizeMake(emotionSize.width, emotionSize.height + kXHHaveBubbleMargin * 2); break; } case XHBubbleMessageMediaTypeVideo: { CGSize needVideoConverPhotoSize = [XHMessageBubbleView neededSizeForPhoto:message.videoConverPhoto]; bubbleSize = CGSizeMake(needVideoConverPhotoSize.width, needVideoConverPhotoSize.height + kXHNoneBubblePhotoMargin * 2); break; } case XHBubbleMessageMediaTypePhoto: { CGSize needPhotoSize = [XHMessageBubbleView neededSizeForPhoto:message.photo]; bubbleSize = CGSizeMake(needPhotoSize.width, needPhotoSize.height + kXHHaveBubblePhotoMargin * 3); break; } case XHBubbleMessageMediaTypeLocalPosition: { // 固定大小,必须的 CGSize localPostionSize = [XHMessageBubbleView neededSizeForLocalPostion]; bubbleSize = CGSizeMake(localPostionSize.width, localPostionSize.height + kXHHaveBubblePhotoMargin * 2); break; } default: break; } return bubbleSize; } #pragma mark - UIAppearance Getters - (UIFont *)font { if (_font == nil) { _font = [[[self class] appearance] font]; } if (_font != nil) { return _font; } return [UIFont systemFontOfSize:16.0f]; } #pragma mark - Getters // 获取气泡的位置以及大小,比如有文字的气泡,语音的气泡,图片的气泡,地理位置的气泡,Emotion的气泡,视频封面的气泡 - (CGRect)bubbleFrame { // 1.先得到MessageBubbleView的实际大小 CGSize bubbleSize = [XHMessageBubbleView getBubbleFrameWithMessage:self.message]; // 2.计算起泡的大小和位置 CGFloat paddingX = 0.0f; if (self.message.bubbleMessageType == XHBubbleMessageTypeSending) { paddingX = CGRectGetWidth(self.bounds) - bubbleSize.width; } XHBubbleMessageMediaType currentMessageMediaType = self.message.messageMediaType; // 最终减去上下边距的像素就可以得到气泡的位置以及大小 CGFloat marginY = kXHHaveBubbleVoiceMargin; CGFloat topSumForBottom = 0.0; switch (currentMessageMediaType) { case XHBubbleMessageMediaTypeVoice: // marginY = kXHHaveBubbleVoiceMargin; topSumForBottom = kXHHaveBubbleVoiceMargin * 2; break; case XHBubbleMessageMediaTypePhoto: case XHBubbleMessageMediaTypeLocalPosition: // marginY = kXHHaveBubblePhotoMargin; topSumForBottom = kXHHaveBubblePhotoMargin * 2; break; case XHBubbleMessageMediaTypeText: topSumForBottom = 10 * 2; break; default: // 视频、表情 marginY = kXHHaveBubbleMargin; topSumForBottom = kXHHaveBubbleMargin * 2; break; } return CGRectMake(paddingX, marginY, bubbleSize.width, bubbleSize.height - topSumForBottom); } #pragma mark - Configure Methods - (void)configureCellWithMessage:(XHMessage *)message { _message = message; [self configureBubbleImageView:message]; [self configureMessageDisplayMediaWithMessage:message]; } - (void)configureBubbleImageView:(XHMessage *)message { XHBubbleMessageMediaType currentType = message.messageMediaType; _voiceDurationLabel.hidden = YES; _voiceUnreadDotImageView.hidden = YES; switch (currentType) { case XHBubbleMessageMediaTypeVoice: { // update by lsz 隐藏语音时间 NO ==》 YES _voiceDurationLabel.hidden = NO; _voiceUnreadDotImageView.hidden = message.isRead; } case XHBubbleMessageMediaTypeText: case XHBubbleMessageMediaTypeEmotion: { _bubbleImageView.image = [XHMessageBubbleFactory bubbleImageViewForType:message.bubbleMessageType style:XHBubbleImageViewStyleWeChat meidaType:message.messageMediaType]; // 只要是文本、语音、第三方表情,背景的气泡都不能隐藏 _bubbleImageView.hidden = NO; // add by lsz 20201206 if (@available(iOS 14.0, *)) { if (message.bubbleMessageType == XHBubbleMessageTypeSending) { _bubbleImageView.layer.cornerRadius = 5.0f; _bubbleImageView.layer.masksToBounds = YES; _bubbleImageView.backgroundColor = RGBA(255, 230, 218, 1); /// 气泡背景颜色 } else { _bubbleImageView.backgroundColor = [UIColor whiteColor]; _bubbleImageView.layer.cornerRadius = 5.0f; _bubbleImageView.layer.masksToBounds = YES; } } // 只要是文本、语音、第三方表情,都需要把显示尖嘴图片的控件隐藏了 _bubblePhotoImageView.hidden = YES; if (currentType == XHBubbleMessageMediaTypeText) { // 如果是文本消息,那文本消息的控件需要显示 _displayTextView.hidden = NO; if(message.bubbleMessageType == XHBubbleMessageTypeSending){ _displayTextView.textColor = KKBlack20; }else{ _displayTextView.textColor = KKBlack20; } // 那语言的gif动画imageView就需要隐藏了 _animationVoiceImageView.hidden = YES; _emotionImageView.hidden = YES; } else { // 那如果不文本消息,必须把文本消息的控件隐藏了啊 _displayTextView.hidden = YES; // 对语音消息的进行特殊处理,第三方表情可以直接利用背景气泡的ImageView控件 if (currentType == XHBubbleMessageMediaTypeVoice) { [_animationVoiceImageView removeFromSuperview]; _animationVoiceImageView = nil; UIImageView *animationVoiceImageView = [XHMessageVoiceFactory messageVoiceAnimationImageViewWithBubbleMessageType:message.bubbleMessageType]; [self addSubview:animationVoiceImageView]; _animationVoiceImageView = animationVoiceImageView; _animationVoiceImageView.hidden = NO; } else { _emotionImageView.hidden = NO; _bubbleImageView.hidden = YES; _animationVoiceImageView.hidden = YES; } } break; } case XHBubbleMessageMediaTypePhoto: case XHBubbleMessageMediaTypeVideo: case XHBubbleMessageMediaTypeLocalPosition: { // 只要是图片和视频消息,必须把尖嘴显示控件显示出来 _bubblePhotoImageView.hidden = NO; _videoPlayImageView.hidden = (currentType != XHBubbleMessageMediaTypeVideo); _geolocationsLabel.hidden = (currentType != XHBubbleMessageMediaTypeLocalPosition); // 那其他的控件都必须隐藏 _displayTextView.hidden = YES; _bubbleImageView.hidden = YES; _animationVoiceImageView.hidden = YES; _emotionImageView.hidden = YES; break; } default: break; } } - (void)configureMessageDisplayMediaWithMessage:(XHMessage *)message { switch (message.messageMediaType) { case XHBubbleMessageMediaTypeText: _displayTextView.attributedText = [[XHMessageBubbleHelper sharedMessageBubbleHelper] bubbleAttributtedStringWithText:message.content]; break; case XHBubbleMessageMediaTypePhoto: _bubblePhotoImageView.message = message; [_bubblePhotoImageView configureMessagePhoto:message.photo thumbnailUrl:message.content originPhotoUrl:message.content onBubbleMessageType:self.message.bubbleMessageType]; break; case XHBubbleMessageMediaTypeVideo: [_bubblePhotoImageView configureMessagePhoto:message.videoConverPhoto thumbnailUrl:message.content originPhotoUrl:message.content onBubbleMessageType:self.message.bubbleMessageType]; break; case XHBubbleMessageMediaTypeVoice: self.voiceDurationLabel.text = [NSString stringWithFormat:@"%d\'\'", [message.duration intValue]]; break; case XHBubbleMessageMediaTypeEmotion: // 直接设置GIF if (message.content) { NSString *imgStr = [NSString stringWithFormat:@"emoji_%@",message.content]; NSData *animatedData = UIImagePNGRepresentation(ImageName_(imgStr)); //NSData *animatedData = [NSData dataWithContentsOfFile:message.emotionPath]; FLAnimatedImage *animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:animatedData]; _emotionImageView.animatedImage = animatedImage; } break; case XHBubbleMessageMediaTypeLocalPosition: [_bubblePhotoImageView configureMessagePhoto:message.localPositionPhoto thumbnailUrl:nil originPhotoUrl:nil onBubbleMessageType:self.message.bubbleMessageType]; _geolocationsLabel.text = message.geolocations; break; default: break; } [self setNeedsLayout]; } - (void)configureVoiceDurationLabelFrameWithBubbleFrame:(CGRect)bubbleFrame { CGRect voiceFrame = _voiceDurationLabel.frame; voiceFrame.origin.x = (self.message.bubbleMessageType == XHBubbleMessageTypeSending ? bubbleFrame.origin.x - CGRectGetWidth(voiceFrame) : bubbleFrame.origin.x + bubbleFrame.size.width); // voiceFrame.origin.y = _bubbleImageView.frame.origin.y;xTT_chat _voiceDurationLabel.frame = voiceFrame; _voiceDurationLabel.center = CGPointMake(_voiceDurationLabel.center.x, _bubbleImageView.center.y); } - (void)configureVoiceUnreadDotImageViewFrameWithBubbleFrame:(CGRect)bubbleFrame { CGRect voiceUnreadDotFrame = _voiceUnreadDotImageView.frame; voiceUnreadDotFrame.origin.x = (self.message.bubbleMessageType == XHBubbleMessageTypeSending ? self.bubbleImageView.frame.origin.x - kXHUnReadDotSize * 3 : CGRectGetMaxX(self.bubbleImageView.frame))+kXHUnReadDotSize; voiceUnreadDotFrame.origin.y = self.bubbleImageView.frame.origin.y + kXHUnReadDotSize / 2.0; // voiceUnreadDotFrame.origin.y = CGRectGetMidY(bubbleFrame) - kXHUnReadDotSize / 2.0; _voiceUnreadDotImageView.frame = voiceUnreadDotFrame; } - (void)configureStatuesImageViewFrameWithBubbleFrame:(CGRect)bubbleFrame { if ([[self.message status] isEqualToNumber:@1] && self.message.bubbleMessageType == XHBubbleMessageTypeSending) { CGRect statuesFrame = _statusImageView.frame; statuesFrame.origin.x = (self.message.bubbleMessageType == XHBubbleMessageTypeSending ? bubbleFrame.origin.x - CGRectGetWidth(statuesFrame) - 5 : bubbleFrame.origin.x + bubbleFrame.size.width + 5); statuesFrame.origin.y = CGRectGetMaxY(bubbleFrame) - CGRectGetHeight(statuesFrame); _statusImageView.frame = statuesFrame; _statusImageView.hidden = NO; }else{ _statusImageView.hidden = YES; } } #pragma mark - Life cycle - (instancetype)initWithFrame:(CGRect)frame message:(XHMessage *)message { self = [super initWithFrame:frame]; if (self) { // Initialization code _message = message; // 1、初始化气泡的背景 if (!_bubbleImageView) { //bubble image FLAnimatedImageView *bubbleImageView = [[FLAnimatedImageView alloc] init]; bubbleImageView.frame = self.bounds; bubbleImageView.userInteractionEnabled = YES; [self addSubview:bubbleImageView]; _bubbleImageView = bubbleImageView; //_bubbleImageView.backgroundColor = UIColor.blueColor; } // 2、初始化显示文本消息的TextView if (!_displayTextView) { SETextView *displayTextView = [[SETextView alloc] initWithFrame:CGRectZero]; displayTextView.textColor = [UIColor colorWithWhite:0.143 alpha:1.000]; displayTextView.backgroundColor = [UIColor clearColor]; displayTextView.selectable = NO; displayTextView.lineSpacing = kXHTextLineSpacing; displayTextView.font = [[XHMessageBubbleView appearance] font]; displayTextView.showsEditingMenuAutomatically = NO; displayTextView.highlighted = NO; [self addSubview:displayTextView]; _displayTextView = displayTextView; } // 3、初始化显示图片的控件 if (!_bubblePhotoImageView) { XHBubblePhotoImageView *bubblePhotoImageView = [[XHBubblePhotoImageView alloc] initWithFrame:CGRectZero]; [self addSubview:bubblePhotoImageView]; _bubblePhotoImageView = bubblePhotoImageView; if (!_videoPlayImageView) { UIImageView *videoPlayImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MessageVideoPlay"]]; [bubblePhotoImageView addSubview:videoPlayImageView]; _videoPlayImageView = videoPlayImageView; } if (!_geolocationsLabel) { UILabel *geolocationsLabel = [[UILabel alloc] initWithFrame:CGRectZero]; geolocationsLabel.numberOfLines = 0; geolocationsLabel.lineBreakMode = NSLineBreakByTruncatingTail; geolocationsLabel.textColor = [UIColor whiteColor]; geolocationsLabel.backgroundColor = [UIColor clearColor]; geolocationsLabel.font = [UIFont systemFontOfSize:12]; [bubblePhotoImageView addSubview:geolocationsLabel]; _geolocationsLabel = geolocationsLabel; } } // 4、初始化显示语音时长的label if (!_voiceDurationLabel) { UILabel *voiceDurationLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 8, 28, 20)]; voiceDurationLabel.textColor = [UIColor colorWithWhite:0.579 alpha:1.000]; voiceDurationLabel.backgroundColor = [UIColor clearColor]; voiceDurationLabel.font = [UIFont systemFontOfSize:13.f]; voiceDurationLabel.textAlignment = NSTextAlignmentCenter; voiceDurationLabel.hidden = YES; [self addSubview:voiceDurationLabel]; _voiceDurationLabel = voiceDurationLabel; } // 5、初始化显示gif表情的控件 if (!_emotionImageView) { FLAnimatedImageView *emotionImageView = [[FLAnimatedImageView alloc] initWithFrame:CGRectZero]; [self addSubview:emotionImageView]; _emotionImageView = emotionImageView; } // 6. 初始化显示语音未读标记的imageview if (!_voiceUnreadDotImageView) { NSString *voiceUnreadImageName = [[XHConfigurationHelper appearance].messageTableStyle objectForKey:kXHMessageTableVoiceUnreadImageNameKey]; if (!voiceUnreadImageName) { voiceUnreadImageName = @"msg_chat_voice_unread"; } UIImageView *voiceUnreadDotImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kXHUnReadDotSize, kXHUnReadDotSize)]; voiceUnreadDotImageView.image = [UIImage imageNamed:voiceUnreadImageName]; voiceUnreadDotImageView.hidden = YES; [self addSubview:voiceUnreadDotImageView]; _voiceUnreadDotImageView = voiceUnreadDotImageView; } if (!_statusImageView) { UIImageView *statusImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; statusImageView.image = [myHelper getImageWithName:@"chat_发送成功"]; [self addSubview:statusImageView]; _statusImageView = statusImageView; } } return self; } - (void)dealloc { _message = nil; _displayTextView = nil; _bubbleImageView = nil; _bubblePhotoImageView = nil; _animationVoiceImageView = nil; _voiceUnreadDotImageView = nil; _statusImageView = nil; _voiceDurationLabel = nil; _emotionImageView = nil; _videoPlayImageView = nil; _geolocationsLabel = nil; _font = nil; } - (void)layoutSubviews { [super layoutSubviews]; XHBubbleMessageMediaType currentType = self.message.messageMediaType; switch (currentType) { case XHBubbleMessageMediaTypeText: case XHBubbleMessageMediaTypeVoice: case XHBubbleMessageMediaTypeEmotion: { // 获取实际气泡的大小 CGRect bubbleFrame = [self bubbleFrame]; self.bubbleImageView.frame = bubbleFrame; if (currentType == XHBubbleMessageMediaTypeText) { CGFloat textX = -(kXHArrowMarginWidth / 2.0); if (self.message.bubbleMessageType == XHBubbleMessageTypeReceiving) { textX = kXHArrowMarginWidth / 2.0; } CGRect displayTextViewFrame = CGRectZero; displayTextViewFrame.size.width = CGRectGetWidth(bubbleFrame) - kXHLeftTextHorizontalBubblePadding - kXHRightTextHorizontalBubblePadding - kXHArrowMarginWidth; displayTextViewFrame.size.height = CGRectGetHeight(bubbleFrame) - kXHHaveBubbleMargin * 2.8; self.displayTextView.frame = displayTextViewFrame; self.displayTextView.center = CGPointMake(self.bubbleImageView.center.x + textX, self.bubbleImageView.center.y); } if (currentType == XHBubbleMessageMediaTypeVoice) { // 配置语音播放的位置 CGRect animationVoiceImageViewFrame = self.animationVoiceImageView.frame; CGFloat voiceImagePaddingX = CGRectGetMaxX(bubbleFrame) - kXHVoiceMargin - CGRectGetWidth(animationVoiceImageViewFrame); if (self.message.bubbleMessageType == XHBubbleMessageTypeReceiving) { voiceImagePaddingX = CGRectGetMinX(bubbleFrame) + kXHVoiceMargin; } animationVoiceImageViewFrame.origin = CGPointMake(voiceImagePaddingX, CGRectGetMidY(bubbleFrame) - CGRectGetHeight(animationVoiceImageViewFrame) / 2.0); // 垂直居中 self.animationVoiceImageView.frame = animationVoiceImageViewFrame; [self configureVoiceDurationLabelFrameWithBubbleFrame:bubbleFrame]; [self configureVoiceUnreadDotImageViewFrameWithBubbleFrame:bubbleFrame]; } if (currentType == XHBubbleMessageMediaTypeEmotion) { CGRect emotionImageViewFrame = bubbleFrame; emotionImageViewFrame.size = [XHMessageBubbleView neededSizeForEmotion]; self.emotionImageView.frame = emotionImageViewFrame; } [self configureStatuesImageViewFrameWithBubbleFrame:bubbleFrame]; break; } case XHBubbleMessageMediaTypePhoto: case XHBubbleMessageMediaTypeVideo: case XHBubbleMessageMediaTypeLocalPosition: { CGSize needPhotoSize = [XHMessageBubbleView neededSizeForPhoto:self.message.photo]; CGFloat paddingX = 0.0f; if (self.message.bubbleMessageType == XHBubbleMessageTypeSending) { paddingX = CGRectGetWidth(self.bounds) - needPhotoSize.width; } CGFloat marginY = kXHNoneBubblePhotoMargin; if (currentType == XHBubbleMessageMediaTypePhoto || currentType == XHBubbleMessageMediaTypeLocalPosition) { marginY = kXHHaveBubblePhotoMargin; } CGRect photoImageViewFrame = CGRectMake(paddingX, marginY, needPhotoSize.width, needPhotoSize.height); self.bubblePhotoImageView.frame = photoImageViewFrame; [self configureStatuesImageViewFrameWithBubbleFrame:photoImageViewFrame]; self.videoPlayImageView.center = CGPointMake(CGRectGetWidth(photoImageViewFrame) / 2.0, CGRectGetHeight(photoImageViewFrame) / 2.0); CGRect geolocationsLabelFrame = CGRectMake(11, CGRectGetHeight(photoImageViewFrame) - 47, CGRectGetWidth(photoImageViewFrame) - 20, 40); self.geolocationsLabel.frame = geolocationsLabelFrame; break; } default: break; } } @end