2018-04-27

最近(我们是做音视频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邮箱。如有不正确的地方也请大家多指教,

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容