最近(我们是做音视频SDK的)项目需要自己实现聊天界面Demo帮助集成我们sdk的用户理解使用方法,不能使用第三方刚开始一脑雾水不知从何下手,经过一番查阅各种网上的聊天界面相关资料,最后还是独自的完成了。做个简单的总结希望帮到你们。
首先给大家看下界面分析下需求
F307E891-C9BB-4552-BD03-9A1184707A03.png
需求如下:
发送方: 如果是第一次发送消息(不区分发送所有人还是私聊)都显示一次对应的消息(example:(发送至 jack (私密))),切换发送人显示一次昵称,如果对接收者一分钟内连续发送消息则不显示昵称,超过一分钟显示昵称。
接收方: 如果是第一次接收消息(不区分发送所有人还是私聊)都显示一次对应的消息(ex:接收到 jack (私密))如果接收到的消息在一分钟内(对方没有切换到所有人)连续收到消息不显示昵称,超过一分钟显示昵称。
因为后天提供的接口上得数据只带以下(因为是音视频sdk所以聊天im只是实现简单的文字聊天,后续可能加更多功能)
@property(nonatomic, strong, readonly) NSDate* timestamp; /**< 消息时间戳,对应用层是本地时间,传输中用GMT时间 */
@property(nonatomic, strong, readonly) AVDUserId fromId; /**< 消息发送者Id */
@property(nonatomic, strong, readonly) NSString* fromName; /**< 消息发送者名 */
@property(nonatomic, strong, readonly) NSString* message; /**< 消息内容 */
界面分析:如何判断是否连续发送给同一个人消息 、如何判断上次发送消息的时间是否超过1分钟 、接收方如果判断是否同一个人在一分钟之内连续发送给你消息 、接收方如何判断接收到的消息是私聊还是公聊 、聊天输入框的自适应高度 、消息cell的文本自适应等等
后台提供的接口如下
*******接收到信息的代理**************
//接受到公聊信息
- (void)receivePublicMessage:(AVDMessage *)message;
//接受到私聊信息
- (void)receivePrivateMessage:(AVDMessage *)message;
*******发送信息方法**************
//发送所有人消息
- (BOOL)sendMessageToAll:(NSString *)message;
//发送私聊消息
- (BOOL)sendMessageToPrivate:(NSString *)message toUser:(AVDUserId)userId;
根据后台提供的这些并不能完成上面界面这样的效果,因为至少消息上还缺少很多东西,解决办法是对AVDMessage进行继承之后,native端自己增加需要属性和字段
继承属性如下
@interface NativeChatMessage : AVDMessage
@property (nonatomic, assign)BOOL isPublic;///<公聊还是私聊
@property (nonatomic, assign)BOOL isSend;///<是否是发送者
@property (nonatomic, assign)BOOL isAccpetPublic;///<接受到私聊还是公聊
@property (nonatomic, assign)BOOL isContinuous;///<是否是连续接收到或发送给同一个人
@property (nonatomic, copy)NSString *receiveId;///<接收者id
@property (nonatomic, assign)BOOL isExceed2Minutes;///<接收或者发送是否超过2分钟
@end
增加这些还不行,因为消息上并不能达到这种效果,这就需要navite端当达到显示信息条件时自己对消息链表插入能区分是聊天信息还是显示的数据,在取出数据时根据数据类型做区分显示
E8C78F6F-E778-41C9-AAE4-745F8F69B6EB.png
界面的实现
聊天输入框的自适应高度
//获取文本最大高度 然后刷新输入框的高度约束(ps输入框是textView)
- (CGFloat)maxTextHeight{
// 计算最大高度 = (每行高度 * 总行数 + 文字上下间距)
_maxTextHeight = ceil(self.inputTextView.font.lineHeight * 4 + self.inputTextView.textContainerInset.top + self.inputTextView.textContainerInset.bottom);
return _maxTextHeight;
}
textView实现placeHolder功能
//kvc 设置placeHolder
- (void)setUpPlaceHolder{
// _placeholderLabel
UILabel *placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.text = @"单击此处聊天或者轻击消息回复";
placeHolderLabel.numberOfLines = 0;
placeHolderLabel.textColor = [UIColor lightGrayColor];
[placeHolderLabel sizeToFit];
[_inputTextView addSubview:placeHolderLabel];
// same font
_inputTextView.font = [UIFont systemFontOfSize:15.f];
placeHolderLabel.font = [UIFont systemFontOfSize:15.f];
[_inputTextView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
}
聊天信息cell我是弄了2中类型的根据message.isSend来加载cell
NativeChatMessage *message = self.mMessageArr[indexPath.row];
if (message.isSend) {
ChatMessageSelfCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentiferSelf];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell setMessageModel:message];
return cell;
}
if(!message.isSend){
ChatMessageOtherCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentiferOther];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell setMessageModel:message];
return cell;
}
return nil;
聊天气泡的拉伸是用的节切片实现的,这样实现起来比较简单不需要计算文字高度然后去拉伸气泡。把气泡图片放到xcode管理图片的Assets.xcassets里面,然后选中图片右下角有个Show Slicing点击就会明白了,需要说明下,气泡的拉伸尽量拉伸中间保证四个角不被拉伸。3D982505-F19A-4F11-90DD-2373EED81710.png
下面这个使用简单的属性字符串就行了,针对我的界面需要我做了一个简单的封装
image.png
/**
* 拼接提示信息
*
* @param isSender 是否是发送方
* @param isPublic 是否是公聊消息
* @param senderName isSender 为NO 设置该值才有效 *
* @return attribute
*/
- (NSMutableAttributedString *)hintMessageIsSender:(BOOL)isSender isPublic:(BOOL)isPublic senderName:(NSString *)senderName{
NSMutableAttributedString *totleString = [[NSMutableAttributedString alloc] init];
NSMutableAttributedString *from = [[NSMutableAttributedString alloc] initWithString:@"发送至 "];
[from addAttribute:NSForegroundColorAttributeName
value:[UIColor darkTextColor]
range:NSMakeRange(0, from.length)];
NSMutableAttributedString *received = [[NSMutableAttributedString alloc] initWithString:@"接收到 "];
[received addAttribute:NSForegroundColorAttributeName
value:[UIColor darkTextColor]
range:NSMakeRange(0, received.length)];
NSMutableAttributedString *public = [[NSMutableAttributedString alloc] initWithString:@"所有人"];
[public addAttribute:NSForegroundColorAttributeName
value:[UIColor darkGrayColor]
range:NSMakeRange(0, public.length)];
NSMutableAttributedString *sendName = [[NSMutableAttributedString alloc] initWithString:senderName];
[sendName addAttribute:NSForegroundColorAttributeName
value:[UIColor darkGrayColor]
range:NSMakeRange(0, sendName.length)];
NSMutableAttributedString *private = [[NSMutableAttributedString alloc] initWithString:@" (私密)"];
[private addAttribute:NSForegroundColorAttributeName
value:[UIColor orangeColor]
range:NSMakeRange(0, private.length)];
if (isSender) {//发送方
[totleString appendAttributedString:from];
}else{//接收到
[totleString appendAttributedString:received];
}
if (isPublic) {//所有人
[totleString appendAttributedString:public];
}else{
[totleString appendAttributedString:sendName];
[totleString appendAttributedString:private];
}
return totleString;
}
消息提示模型根据消息类型和判断条件对链表进行数据插入(ps 消息发送和接收到信息都会到这里来,所有提出了一个公共方法做统一处理,这样方便排查问题出在哪里,逻辑也会比较清楚)
- (void)reloadTableAndScrollToBottom:(NativeChatMessage *)message{
if ((message.isSend && !self.isSendSameUser) || (message.isExceed2Minutes && message.isSend)) {//自己是发送方并且不是发送给同一个人
UserNameSend *model = [[UserNameSend alloc]init];
model.receivedName = [self hintMessageIsSender:YES isPublic:[self.selRecevierBtn.titleLabel.text isEqualToString:@"所有人"] senderName:self.selRecevierBtn.titleLabel.text];
model.messageTime = [self date2string:[NSDate date]];
[self.mMessageArr addObject:model];
}
if (!message.isSend) {//接受到消息
if (message.isAccpetPublic) {//公聊消息
}
if (!message.isAccpetPublic) {//私聊消息
}
if (![self isContinuousSameSendId:message.fromId] || ![self isContinuousSameReceivedId:message.receiveId] || message.isExceed2Minutes) {//接收者发送改变 或者发送者改变 或者时间超过2分钟
NSLog(@"新消息 fromId = %@ fromeName = %@ content = %@ time = %@",message.fromId, message.fromName, message.message ,[self date2string:[NSDate dateWithTimeIntervalSince1970:self.lastTimeReceived]]);
UserNameReceived *model = [[UserNameReceived alloc]init];
model.receivedName = [self hintMessageIsSender:message.isSend isPublic:message.isAccpetPublic senderName:message.fromName];
model.messageTime = [self date2string:[NSDate dateWithTimeIntervalSince1970:self.lastTimeReceived]];
[self.mMessageArr addObject:model];
}
}
[self.mMessageArr addObject:message];
[self.tableView reloadData];
if (self.mMessageArr.count != 0) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.mMessageArr.count - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
最后给大家看下完成之后的效果聊天界面效果.gif
需要代码的同学我们sdk的demo会放到github上,目前还在功能完善中,急需的请留下qq邮箱。如有不正确的地方也请大家多指教,