You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
624 lines
27 KiB
624 lines
27 KiB
// |
|
// 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
|
|
|