iOS开发之融云IM集成

首先去融云开发者注册一波账号,获取AppKey :


AppKey.png

然后拉取融云SDK:

platform :ios, '8.0'
inhibit_all_warnings!
target 'your projectName' do
pod 'RongCloudIM/IMKit'
end

然后pod install,会发现SDK已经被你拉取下来了,首先在程序入口AppDelegate.m里初始化融云SDK:

    [[RCIM sharedRCIM]initWithAppKey:kRongCloudKey];
    [RCIM sharedRCIM].connectionStatusDelegate = self;
    [RCIM sharedRCIM].enablePersistentUserInfoCache = YES;
    [RCIM sharedRCIM].globalConversationAvatarStyle = RC_USER_AVATAR_CYCLE;
    [RCIM sharedRCIM].globalMessageAvatarStyle =RC_USER_AVATAR_CYCLE;
    [RCIM sharedRCIM].showUnkownMessageNotificaiton = YES;
    [RCIM sharedRCIM].enableMessageAttachUserInfo = YES;
    [RCIM sharedRCIM].userInfoDataSource = self;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectToRongCloud) name:kLoginSucessNotification object:nil];

// MARK: - RCIMUserInfoDataSource
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void (^)(RCUserInfo *userInfo))completion {
    NSLog(@"Rong cloud userID: %@", userId);
    if ([userId isEqualToString:kAppUserInfo.userId]) {
        RCUserInfo *user = [[RCUserInfo alloc] initWithUserId:userId
                                                         name:kAppUserInfo.name
                                                     portrait:nil];
       //缓存至用户信息
        [kRongCloudManager insertWithUser:user];
        completion(user);
    } else {
        __block RCUserInfo *user;
        //根据userId去查找相对应的头像和昵称,缓存
        [NetTool postWithURLString:[NSString stringWithFormat:@"%@%@",kBaseURL,@"rongCloud/getUserNameAndImage"]
                        parameters: [UtilityHelper encryptParmar:@{@"uid":userId}]
                           success:^(NSDictionary *data) {
            if ([data[@"reCode"] isEqualToString:@"X0000"]) {
                user = [[RCUserInfo alloc] initWithUserId:userId
                                                                 name:data[@"rel"][@"name"]
                                                             portrait:[data[@"rel"][@"image"]length] > 2 ? data[@"rel"][@"image"]: nil];
             //缓存至用户信息
                [kRongCloudManager insertWithUser:user];
                completion(user);
            } else {
                completion(user);
            }
        } failure:^(NSError *error) {
            completion(user);
        }];
    }
}

然后在App的登录那里将后台获取到的融云token信息拿过来去连接融云服务器:

// MARK: - 融云服务器连接状态监听
- (void)onRCIMConnectionStatusChanged:(RCConnectionStatus)status {
   if (status == ConnectionStatus_Connected) {
       NSLog(@"融云服务器连接成功!");
   } else  {
       if (status == ConnectionStatus_SignUp) {
           NSLog(@"融云服务器断开连接!");
       } else {
           NSLog(@"融云服务器连接失败!");
       }
   }
}
// MARK: - 连接融云服务器
- (void)connectToRongCloud {
   [NetTool postWithURLString:[NSString stringWithFormat:@"%@%@",kBaseURL,@"rongCloud/getRongCloudToken"]
                   parameters:@{@"token":kUserToken}
                      success:^(NSDictionary *data) {
                          if ([data[@"reCode"] isEqualToString:@"X0000"]) {
                              NSString *rongCloudToken = data[@"rel"];
                              [[NSUserDefaults standardUserDefaults] setObject:rongCloudToken forKey:@"RCToken"];
                              [[NSUserDefaults standardUserDefaults] synchronize];
                              [[RCIM sharedRCIM] connectWithToken:rongCloudToken
                                                          success:^(NSString *userId) {
                                                              //设置当前用户信息
                                                              RCUserInfo *currentUserInfo = [[RCUserInfo alloc] initWithUserId:kAppUserInfo.userId
                                                                                                                          name:kAppUserInfo.name
                                                                                                                      portrait:kRandomAvatar];
                                                              [RCIM sharedRCIM].currentUserInfo = currentUserInfo;
                                                              [RCIM sharedRCIM].enablePersistentUserInfoCache = YES;
                                                          } error:^(RCConnectErrorCode status) {
                                                              
                                                          } tokenIncorrect:^{
                                                              
                                                          }];
                          }
                      } failure:^(NSError *error) {
                          
                      }];
}

在登录的页面那里发一个通知给App,拿到Token去连接融云的服务器:

  [[NSNotificationCenter defaultCenter] postNotificationName:kLoginSucessNotification object:nil];

看融云给出的打印日志会发现,融云的集成是成功了:


屏幕快照 2019-01-14 下午5.38.02.png

接下来就是聊天模块了,仔细看SDK的头文件你会发现,其实融云的程序猿早就写好了这些模块,如果不是高度自定义的话,完全可以new一个VController继承他写好的控制器,其中最核心的两个viewController:RCConversationViewController、RCConversationListViewController,顾明思义,第一个是用来展示聊天会话的,第二个是用来展示绘画列表的。
具体的实现代码如下:

@interface CustomChatListVC () <RCIMReceiveMessageDelegate,UITableViewDataSource,UITableViewDelegate> {
    UITableView *_tableView;
    NSMutableArray <RCConversation *> * _conversationListArray;
}

@end

@implementation CustomChatListVC

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self refresh];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    _conversationListArray = [NSMutableArray arrayWithArray:[[RCIMClient sharedRCIMClient] getConversationList:@[@(ConversationType_PRIVATE)]]];
    [RCIM sharedRCIM].receiveMessageDelegate = self;
    _tableView = kTableViewWithConfiguration(self.view, CGRectZero, 0, self, self, nil);
    [_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
}

// MARK: - refreshAction
- (void)refresh {
    _conversationListArray = [NSMutableArray arrayWithArray:[[RCIMClient sharedRCIMClient] getConversationList:@[@(ConversationType_PRIVATE)]]];
    for (RCConversation *conversation in _conversationListArray) {
        __block RCUserInfo *user;
        [NetTool postWithURLString:[NSString stringWithFormat:@"%@%@",kBaseURL,@"rongCloud/getUserNameAndImage"]
                        parameters: [UtilityHelper encryptParmar:@{@"uid":conversation.targetId}]
                           success:^(NSDictionary *data) {
                               if ([data[@"reCode"] isEqualToString:@"X0000"]) {
                                   user = [[RCUserInfo alloc] initWithUserId:conversation.targetId
                                                                        name:data[@"rel"][@"name"]
                                                                    portrait:[data[@"rel"][@"image"]length] > 2 ? data[@"rel"][@"image"]: nil];
                                   [kRongCloudManager insertWithUser:user];
                                   dispatch_async(dispatch_get_main_queue(), ^{
                                       [_tableView reloadData];
                                   });
                               }
                           } failure:^(NSError *error) {
                           }];
    }
}

- (RCUserInfo *)getSenderInfoWithExtra:(NSString *)extraStr targetId:(NSString *)targetId {
    NSDictionary *json = [extraStr mj_JSONObject];
    return [[RCUserInfo alloc] initWithUserId:targetId name:json[@"name"] portrait:json[@"avatar"]];
}


// MARK: - 接收到消息的回调
- (void)onRCIMReceiveMessage:(RCMessage *)message left:(int)left {
    /*NSInteger index = 0;
    BOOL exsit = NO;
    
    for (int i = 0 ; i < _conversationListArray.count; i ++) {
        RCConversation *conversation = _conversationListArray[i];
        if ([conversation.targetId isEqualToString:message.targetId]) {
            exsit = YES;
            index = i;
            break;
        }
    }
    if (exsit) {
        //存在
        RCConversation *conversation = _conversationListArray[index];
        conversation.lastestMessage = message.content;
        conversation.unreadMessageCount ++;
        [_conversationListArray replaceObjectAtIndex:index withObject:conversation];
        dispatch_async(dispatch_get_main_queue(), ^{
            [_tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
        });
    } else {
        RCConversation *newConversation = [RCConversation new];
        newConversation.lastestMessage = message.content;
        newConversation.targetId = message.targetId;
        newConversation.senderUserId = message.senderUserId;
        newConversation.receivedTime = message.receivedTime;
        newConversation.unreadMessageCount ++;
        [_conversationListArray addObject:newConversation];
        dispatch_async(dispatch_get_main_queue(), ^{
            [_tableView reloadData];
        });
    }*/
    [self refresh];
}

// MARK: - UITableVieDataSource &Delegate

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _conversationListArray.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ChatListCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([ChatListCell class])];
    if (!cell) {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ChatListCell class]) owner:self options:nil] firstObject];
    }
    [cell setConversationListModel:_conversationListArray[indexPath.row]];
    return cell;
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    RCConversation *model = _conversationListArray[indexPath.row];
    model.unreadMessageCount = 0;
    [_conversationListArray replaceObjectAtIndex:indexPath.row withObject:model];
    CustomChatVC *conversationVC = [[CustomChatVC alloc]initWithConversationType:model.conversationType targetId:model.targetId];
    conversationVC.title = [kRongCloudManager getUserWithTargetId:model.targetId].name;
    [self.navigationController pushViewController:conversationVC animated:YES];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80;
}

自定义列表cell:

@implementation ChatListCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
    //已读已发送
    _tip.clipsToBounds = YES;
    _tip.layer.borderWidth = 1.0;
    _tip.layer.cornerRadius = 4.0;
    //头像
    _avatar.clipsToBounds = YES;
    _avatar.layer.cornerRadius = _avatar.height / 2.0;
    //未读消息
    _remindCount.clipsToBounds = YES;
    _remindCount.layer.cornerRadius = _remindCount.height / 2.0;
    _remindCount.hidden = YES;
}

/*
- (void)setDataWithConversationListModel:(ConversationListModel *)conversationListModel {
    RCConversation *model = conversationListModel.conversation;
    if (model.unreadMessageCount == 0 ) {
        _remindCount.hidden = YES;
    } else {
        _remindCount.hidden = NO;
        _remindCount.text = [NSString stringWithFormat:@"%i",model.unreadMessageCount];
    }
    if (model.receivedStatus == ReceivedStatus_READ) {
        //已读
        _tip.layer.borderColor = [UIColor greenColor].CGColor;
        _tip.textColor = [UIColor greenColor];
        _tip.text = @"已读";
    } else {
        _tip.layer.borderColor = [UIColor redColor].CGColor;
        _tip.textColor = [UIColor redColor];
        _tip.text = @"送达";
    }
    RCUserInfo *sender = [kRongCloudManager getUserWithTargetId:model.targetId];
    [_avatar sd_setImageWithURL:[NSURL URLWithString:sender.portraitUri] placeholderImage:[UIImage imageWithColor:[UIColor grayColor] withSize:CGSizeMake(60, 60)]];
    //名字
    _name.text = sender.name;
    //职位名称
    _position.text = sender.name;
    _message.text = [model.lastestMessage valueForKey:@"_content"];
    NSDateFormatter *format = [NSDateFormatter new];
    BOOL today = [[NSCalendar currentCalendar] isDateInToday:[NSDate dateWithTimeIntervalSince1970:model.receivedTime/1000]];
    if (today) {
        [format setDateFormat:@"HH:MM"];
    } else {
        [format setDateFormat:@"yyyy-MM-dd"];
    }
    _time.text = [format stringFromDate:[NSDate dateWithTimeIntervalSince1970:model.receivedTime/1000]];
}
*/

// MARK: - private Method
- (void)setConversationListModel:(RCConversation *)model {
    if (model.unreadMessageCount == 0 ) {
        _remindCount.hidden = YES;
    } else {
        _remindCount.hidden = NO;
        _remindCount.text = [NSString stringWithFormat:@"%i",model.unreadMessageCount];
    }
    if (model.receivedStatus == ReceivedStatus_READ) {
        //已读
        _tip.layer.borderColor = [UIColor greenColor].CGColor;
        _tip.textColor = [UIColor greenColor];
        _tip.text = @"已读";
    } else {
        _tip.layer.borderColor = [UIColor redColor].CGColor;
        _tip.textColor = [UIColor redColor];
        _tip.text = @"送达";
    }
//    RCUserInfo *sender = [self getSenderInfoWithExtra:[model.lastestMessage valueForKey:@"_extra"] targetId:model.targetId];
    RCUserInfo *sender = [kRongCloudManager getUserWithTargetId:model.targetId];
    //头像
    [_avatar sd_setImageWithURL:[NSURL URLWithString:sender.portraitUri] placeholderImage:[UIImage imageWithColor:[UIColor grayColor] withSize:CGSizeMake(60, 60)]];
    //名字
    _name.text = sender.name;
    //职位名称
    _position.text = sender.name;
    BOOL isMe = [model.lastestMessage.senderUserInfo.userId isEqualToString:kAppUserInfo.userId];
    NSString *messageContent = [MessageConvert messageConvertWithMessageContent:model.lastestMessage];
    _message.text = isMe?[NSString stringWithFormat:@"我:%@",messageContent]: messageContent;
    NSDateFormatter *format = [NSDateFormatter new];
   BOOL today = [[NSCalendar currentCalendar] isDateInToday:[NSDate dateWithTimeIntervalSince1970:model.receivedTime/1000]];
    if (today) {
        [format setDateFormat:@"HH:MM"];
    } else {
        [format setDateFormat:@"yyyy-MM-dd"];
    }
    _time.text = [format stringFromDate:[NSDate dateWithTimeIntervalSince1970:model.receivedTime/1000]];
}

- (RCUserInfo *)getSenderInfoWithExtra:(NSString *)extraStr targetId:(NSString *)targetId {
    NSDictionary *json = [extraStr mj_JSONObject];
    return [[RCUserInfo alloc] initWithUserId:targetId name:json[@"name"] portrait:json[@"avatar"]];
}

MessageConvert.m:

+(NSString *)messageConvertWithMessageContent:(RCMessageContent *)messageContent {
    NSString *value = @"";
    if ([messageContent isKindOfClass:[RCTextMessage class]]) {
        //文本
        value = ((RCTextMessage *)messageContent).content;
    } else if ([messageContent isKindOfClass:[RCLocationMessage class]]) {
        //位置
        value = @"[位置]";
    } else if ([messageContent isKindOfClass:[RCImageMessage class]]) {
        //图片
        value = @"[图片]";
    } else if ([messageContent isKindOfClass:[RCVoiceMessage class]]) {
        //语音
        value = @"[语音]";
    } else if ([messageContent isKindOfClass:[RCSightMessage class]]) {
        //小视频
        value = @"[小视频]";
    }
    return value;
}

由于继承自RCConversationListViewController的ViewController只能在dataSource里面添加custom样式的model,从而达到增加自定义cell的效果:

-(NSMutableArray *)willReloadTableData:(NSMutableArray *)dataSource {
    NSArray *customItems = @[@"系统消息1",@"系统消息2"];
    for (int i = 0; i<customItems.count; i++) {
        RCConversationModel *model = [RCConversationModel new];
        model.conversationTitle = customItems[i];
        model.conversationModelType = RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION;
        model.isTop = YES;
        if (dataSource.count) {
            //插入自定义cell
            [dataSource insertObject:model atIndex:i];
        }
    }
    return dataSource;
}

- (void)onSelectedTableRow:(RCConversationModelType)conversationModelType
         conversationModel:(RCConversationModel *)model
               atIndexPath:(NSIndexPath *)indexPath {
    if (model.conversationModelType == RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION) {
        //自定义消息cell的点击
    } else  {
        //系统会话列表cell的点击
    }
}

- (RCConversationBaseCell *)rcConversationListTableView:(UITableView *)tableView
                                  cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    SystemChatListCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SystemChatListCell class])];
    if (!cell) {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([SystemChatListCell class]) owner:self options:nil] firstObject];
    }
    //系统消息cell的配置
    return cell;
}

这个刚开始发现的时候,感觉有点坑,他不像环信那样可以直接去重写baseCell里面的东西(只能添加,而不能修改,因为这个VC继承了RCConversationListViewController),各位大兄弟要注意这个地方。
在聊天室要用到自定义的cell,所有我们要重写一个message消息类集成于RCMessageContent,重写一个Cell继承RCMessageCell,具体如下:

#import <RongIMKit/RongIMKit.h>
#import "JobMessage.h"
NS_ASSUME_NONNULL_BEGIN

@interface JobCell : RCMessageCell

@property(nonatomic, strong) UIImageView *bubbleBackgroundView;

-(void)setDataModel:(RCMessageModel *)model;

//+ (CGSize)bubbleSizeWithJobMessageModel:(JobMessage *)model;

@end

NS_ASSUME_NONNULL_END
#import "JobCell.h"

@interface JobCell () {
    UILabel *_titles[7];
}

@end
@implementation JobCell

+ (CGSize)sizeForMessageModel:(RCMessageModel *)model
      withCollectionViewWidth:(CGFloat)collectionViewWidth
         referenceExtraHeight:(CGFloat)extraHeight {
    return CGSizeMake(kScreenWidth, 300 + extraHeight);
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self initialize];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    self.bubbleBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
    self.bubbleBackgroundView.backgroundColor = [UIColor whiteColor];
    self.bubbleBackgroundView.clipsToBounds = YES;
    self.bubbleBackgroundView.layer.cornerRadius = 5.0;
    [self.messageContentView addSubview:self.bubbleBackgroundView];
    
    for (int i = 0 ; i < 7; i ++) {
        _titles[i] =  kLabelWithCornerRadius(self.bubbleBackgroundView, CGRectMake(20, i == 0 ? 30 :_titles[i - 1].bottom + 10, 280, 35), [UIColor whiteColor], [UIColor blackColor], 0, kFont(14),@"" , 0.0);
    }
    self.bubbleBackgroundView.userInteractionEnabled = YES;
    UITapGestureRecognizer *longPress =
    [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
    [self.bubbleBackgroundView addGestureRecognizer:longPress];
}

- (void)tapAction:(UIGestureRecognizer *)gestureRecognizer {
    if ([self.delegate respondsToSelector:@selector(didTapMessageCell:)]) {
        [self.delegate didTapMessageCell:self.model];
    }
}

- (void)setDataModel:(RCMessageModel *)model {
    [super setDataModel:model];
    JobMessage *message = (JobMessage *)model.content;
    NSDictionary *value = [message.content mj_JSONObject][@"data"];
    for (int i = 0; i < 7; i ++) {
        _titles[i].text =  [NSString stringWithFormat:@"%@:%@\n",[value allKeys][i],[value valueForKey:[value allKeys][i]]];
    }
    
    if (message) {
        
    }
    //拉伸图片
    if (MessageDirection_RECEIVE == self.messageDirection) {
        
        
    } else {
        
    }
}

+ (CGSize)bubbleSizeWithJobMessageModel:(JobMessage *)message {
    return CGSizeMake(300, 300);
}

JobMessage类:

#import <RongIMLib/RongIMLib.h>
#import <RongIMLib/RCMessageContent.h>

NS_ASSUME_NONNULL_BEGIN

@interface JobMessage : RCMessageContent <NSCoding,RCMessageContentView>

@property(nonatomic,strong)NSString *content;

@property(nonatomic, strong) NSString* extra;

+(instancetype)messageWithContent:(NSString *)jsonContent;

@end

NS_ASSUME_NONNULL_END
@implementation JobMessage

+ (instancetype)messageWithContent:(NSString *)jsonContent {
    JobMessage *model = [JobMessage new];
    if (model) {
        model.content = jsonContent;
    }
    
    return model;
}

//存储状态和是否计入未读数
+(RCMessagePersistent)persistentFlag {
    //存储并计入未读数
    return (MessagePersistent_ISCOUNTED);
}

#pragma mark – NSCoding protocol methods
#define KEY_TXTMSG_CONTENT @"content"
#define KEY_TXTMSG_EXTRA @"extra"

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self) {
        self.content = [aDecoder decodeObjectForKey:KEY_TXTMSG_CONTENT];
        self.extra = [aDecoder decodeObjectForKey:KEY_TXTMSG_EXTRA]; }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.content forKey:KEY_TXTMSG_CONTENT];
    [aCoder encodeObject:self.extra forKey:KEY_TXTMSG_EXTRA];
    
}

#pragma mark – RCMessageCoding delegate methods
///将消息内容编码成json
-(NSData *)encode {
    
    NSMutableDictionary *dataDict=[NSMutableDictionary dictionary];
    [dataDict setObject:self.content forKey:@"content"];
    if (self.extra) {
        [dataDict setObject:self.extra forKey:@"extra"];
    }
    
    NSData *data = [NSJSONSerialization dataWithJSONObject:dataDict
                                                   options:kNilOptions
                                                     error:nil];
    
    return data;
}

//将json解码生成消息内容
-(void)decodeWithData:(NSData *)data {
    __autoreleasing NSError* __error = nil;
    if (!data) {
        return;
    }
    NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data
                                                               options:kNilOptions
                                                                 error:&__error];
    NSLog(@"dictionary == %@",dictionary);
    if ([dictionary objectForKey:@"content"]) {
        self.content = dictionary[@"content"];
        NSLog(@"dictionary1111 == %@",dictionary[@"content"]);
        self.extra = dictionary[@"extra"];
    }else{
        NSError *error;
        NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:kNilOptions error:nil];
        NSString *content ;
        if (!data) {
            NSLog(@"%@",error);
        }else{
            content = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        }
        self.content = content;
    }
    
}
//您定义的消息类型名,需要在各个平台上保持一致,以保证消息互通,别以 RC 开头,以免和融云系统冲突
+(NSString *)getObjectName {
    return @"JobMessageType";
}

//最后一条消息是自定义消息的时候,可以更改在会话列表显示的类型,为了区分消息类型
- (NSString *)conversationDigest {
    NSString *contentStr = @"[职位信息]";
    return contentStr;
}

@end

然后在ChatVC里注册对应的message消息类和messageCell类:

 [self registerClass:[JobCell class] forMessageClass:[JobMessage class]];

在AppDelegate里面注册一波自定义message类型:

    [[RCIM sharedRCIM] registerMessageType:[JobMessage class]];

这里特别补充一下关于用户头像和昵称的问题,由于和环信一样,这种IM三方并不负责存储用户或群组信息,这些东西一般是用来存放在自己App的后台服务器上,在加载会话列表的时候需要去后台根据targetId去拉取聊天对话的用户信息,但是有一种更为巧妙的方法就是将发送者的信息(昵称、头像、职位名称等等)存放在消息体中,消息体中有一个字段叫做extra:

{"content":"测试","extra":{
     "name":"Chan",
     "avatar":"http://xxxx/pic_201808241707108698543.png"}
     }

然后根据每条消息的latestMessage.extra字段获取最新一条消息的用户信息,但是注意,这里有一个问题就是加入我一直给一个用户发送消息,但是对方没回复,这时候就不管用了,所以读者也注意到了我上面的代码,用了一个单利对象专门用来缓存对话targeId的RCUserInfo用户信息:

#import "RCUserManager.h"

@implementation RCUserManager

+ (instancetype)shareManager {
    static RCUserManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[RCUserManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    if (self = [super init]) {
        _users = [NSMutableArray new];
    }
    return self;
}

- (void)insertWithUser:(RCUserInfo *)user {
    if (_users) {
        BOOL exsit = NO;
        int index = 0;
        for (int i = 0 ; i < _users.count; i ++) {
            if ([((RCUserInfo *)_users[i]).userId isEqualToString:user.userId]) {
                exsit = YES;
                index = i;
                break;
            }
        }
        if (exsit) {
            [_users replaceObjectAtIndex:index withObject:user];
        } else {
            [_users  addObject:user];
        }
    }
}

- (RCUserInfo *)getUserWithTargetId:(NSString *)targetId {
    RCUserInfo *user;
    if (_users.count) {
        for (int i = 0 ; i < _users.count; i ++) {
            RCUserInfo *user = _users[i];
            if ([user.userId isEqualToString:targetId]) {
                return user;
                break;
            }
        }
    }
    return user;
}

由于自己的项目是招聘类App,在和企业沟通的时候,进入聊天室的时候,会出现一条企业发送过来的职位消息,也就是上面自定义的messageCell:

CustomChatVC *conversationVC = [[CustomChatVC alloc]initWithConversationType:ConversationType_PRIVATE targetId:@"1392428"];
            JobInfoModel *jobModel = [JobInfoModel mj_objectWithKeyValues:jsonValue[@"data"]];
            conversationVC.jobInfoModel = jobModel;
            //插入一条对方发送过来的消息
            [[RCIMClient sharedRCIMClient] insertIncomingMessage:ConversationType_PRIVATE
                                                        targetId:@"1392428"
                                                    senderUserId:@"1392428"
                                                  receivedStatus:ReceivedStatus_READ
                                                         content:[JobMessage messageWithContent:[jsonValue mj_JSONString]]
                                                        sentTime:0];
            conversationVC.title = @"Chan";
            [self.navigationController pushViewController:conversationVC animated:YES];

具体的简单实现就到这里,大致效果如下:


ChatListCell.png

ChatCell.png

由于聊天室使用的是融云的控制器,也可以进行自定义,但是UI界面方面处理起来比较麻烦,后续再完善一波。

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

推荐阅读更多精彩内容

  • 一、iOS 直播聊天室 Demo 说明 1、源码结构 2、AppDelegate 在 AppDelegate 中初...
    ajiao焦阅读 2,829评论 2 7
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,090评论 1 32
  • 见君第一眼,羞涩情怯脸微红。 见君第二眼,恐别后相思甚苦。 见君第三眼,满眼情波意难容。 见君第四眼,已是真心爱相...
    木南楠欣阅读 349评论 0 1
  • 醒来觉得甚是爱你 原创作者:王清华 一 醒来觉得甚是爱你 这句话,是朱生豪写给妻子宋清如的。 直...
    王清华阅读 404评论 3 9
  • 机会是难得的,对于其他人,或许只是一次机遇,而对于我,却是安心与立命。看不清前方的路,只有祈祷了,愿上天护佑,一切顺利!
    一言一瑾阅读 606评论 0 0