无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称和头像。运行环信的demo app,注册用户是直接使用环信ID(username)作为用户名,但是在我们实际应用中,需要将自有用户体系的UserId生成GUID作为环信ID(username)
【参考:http://docs.easemob.com/im/100serverintegration/20users】,
这时候如果不经过处理,则会显示如下界面:


ios开发 头像裁切 苹果开发者头像_#define


那么如何处理,才能显示正确的用户昵称和头像呢?
其实官方已经提供有解决方案了,只不过没有给出示例代码而已。
http://docs.easemob.com/im/490integrationcases/10nickname


引用一下关键文字:

方法二:从消息扩展中获取昵称和头像

昵称和头像的获取:把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。当显示昵称和头像时,请从本地或者缓存中读取,不要直接从消息中把赋值拿给界面(否则当用户昵称改变后,同一个人会显示不同的昵称)。
昵称和头像的更新:当扩展消息中的昵称和头像 URI 与当前本地数据库和缓存中的相应数据不同的时候,需要把新的昵称保存到本地数据库和缓存,并下载新的头像并保存到本地数据库和缓存。


没错,官方提供了两种思路,鉴于项目的实际情况,我选择了【方法二】。

于是,无论安卓,还是IOS,我们都是这样处理的:
【1】.APP间传递用户属性信息:发送(文本、图片…)消息时,要在消息扩展(message.ext)中附带当前用户的属性信息;
【2】.本地缓存用户信息:在接收消息的回调函数里,读取消息扩展(message.ext)里用户属性(键值对)信息;如果本地缓存(sqlite)不存在该用户,则新增缓存记录,如果存在,则更新记录;(用户登录或注册成功后,也要更新用户缓存信息)
【3】.获取用户属性信息:在需要显示昵称的地方,根据环信ID,读取sqlite缓存数据,获取用户昵称和头像;

用户属性信息:ChatUserId(环信ID),ChatUserNick(用户昵称), ChatUserPic(用户头像,完整的url地址);


IOS关键代码:

// 环信聊天用的昵称和头像(发送聊天消息时,要附带这3个属性)
#define kChatUserId @"ChatUserId"// 环信账号
#define kChatUserNick @"ChatUserNick"
#define kChatUserPic @"ChatUserPic"

ChatUserCacheInfo是环信用户信息缓存管理类
ChatUserCacheInfo.h

#import <Foundation/Foundation.h>

@interface ChatUserCacheInfo : NSObject
@property(nonatomic,copy)NSString* Id;
@property(nonatomic,copy)NSString* NickName;
@property(nonatomic,copy)NSString* AvatarUrl;
@end


@interface ChatUserCacheUtil : NSObject

+(void)saveInfo:(NSString *)openId
              imgId:(NSString*)imgId
           nickName:(NSString*)nickName;

+(void)saveDict:(NSDictionary *)userinfo;

+(void)saveModel:(UserApiModel*)user;

+(ChatUserCacheInfo*)queryById:(NSString *)userid;

@end

ChatUserCacheUtil.m

#import "ChatUserCacheUtil.h"
#import "FMDB.h"

#define DBNAME @"cache_data.db"

@implementation ChatUserCacheInfo

@end

@implementation ChatUserCacheUtil

+(void)createTable:(FMDatabase *)db
{
    if ([db open]) {
        if (![db tableExists :@"userinfo"]) {
            if ([db executeUpdate:@"create table userinfo (userid text, username text, userimage text)"]) {
                NSLog(@"create table success");
            }else{
                NSLog(@"fail to create table");
            }
        }else {
             NSLog(@"table is already exist");
        }
    }else{
        NSLog(@"fail to open");
    }
}

+ (void)clearTableData:(FMDatabase *)db
{
    if ([db executeUpdate:@"DELETE FROM userinfo"]) {
        NSLog(@"clear successed");
    }else{
        NSLog(@"fail to clear");
    }
}

+(FMDatabase*)getDB{
    NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *dbPath   = [docsPath stringByAppendingPathComponent:DBNAME];
    FMDatabase *db     = [FMDatabase databaseWithPath:dbPath];
    [self createTable:db];
    return db;
}

+(void)saveModel:(UserApiModel*)user{
    [ChatUserCacheUtil saveInfo:user.EaseMobUserName imgId:user.HeadImg nickName:user.Username];
}

+(void)saveInfo:(NSString *)openId
              imgId:(NSString*)imgId
           nickName:(NSString*)nickName{
    NSMutableDictionary *extDic = [NSMutableDictionary dictionary];
    [extDic setValue:openId forKey:kChatUserId];
    [extDic setValue:@"[url=http://img.baidu.com"]http://img.baidu.com/"[/url]+imgId   forKey:kChatUserPic];//完整图片路径"http://img.baidu.com/1234"。如果imgId是相对路径,那完整路径就是类似"http://img.baidu.com/abc.jpg"
    [extDic setValue:nickName forKey:kChatUserNick];
    [ChatUserCacheUtil saveDict:extDic];
}

+(void)saveDict:(NSDictionary *)userinfo{
    FMDatabase *db     = [self getDB];

    NSString *userid = [userinfo objectForKey:kChatUserId];
    if ([db executeUpdate:@"DELETE FROM userinfo where userid = ?", userid]) {
        DLog(@"删除成功");
    }else{
        DLog(@"删除失败");
    }
    NSString *username = [userinfo objectForKey:kChatUserNick];
    NSString *userimage = [userinfo objectForKey:kChatUserPic];
    if ([db executeUpdate:@"INSERT INTO userinfo (userid, username, userimage) VALUES (?, ?, ?)", userid,username,userimage]) {
        DLog(@"插入成功");
    }else{
        DLog(@"插入失败");
    }

//    NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
    FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
    if ([rs next]) {
        NSString *userid = [rs stringForColumn:@"userid"];
        NSString *username = [rs stringForColumn:@"username"];
        NSString *userimage = [rs stringForColumn:@"userimage"];
        DLog(@"查询一个 %@ %@ %@",userid,username,userimage);
    }

    rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo"];
    while ([rs next]) {
        NSString *userid = [rs stringForColumn:@"userid"];
        NSString *username = [rs stringForColumn:@"username"];
        NSString *userimage = [rs stringForColumn:@"userimage"];
        DLog(@"查询所有 %@ %@ %@",userid,username,userimage);
    }
    [rs close];
//    NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
    [db close];

}

+(ChatUserCacheInfo*)queryById:(NSString *)userid{
    FMDatabase *db     = [self getDB];
    if ([db open]) {
        FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
        if ([rs next]) {

            ChatUserCacheInfo *userInfo = [[ChatUserCacheInfo alloc] init];

            userInfo.Id = [rs stringForColumn:@"userid"];
            userInfo.NickName = [rs stringForColumn:@"username"];
            userInfo.AvatarUrl = [rs stringForColumn:@"userimage"];
            DLog(@"查询一个 %@",userInfo);
            return userInfo;
        }else{
            return nil;
        }
    }else{
        return nil;
    }
}


@end

首先要在用户登录或注册成功后,返回用户登录信息时,缓存一下用户信息:

@protocol UserApiModel <NSObject>

@end

@interface UserApiModel : BaseJSONModel
@property(nonatomic, assign)int Id;
@property(nonatomic, copy)NSString *Username;
@property(nonatomic, copy)NSString *Email;
@property(nonatomic, copy)NSString *HeadImg;
@property(nonatomic, copy)NSString *EaseMobUserName;
@property(nonatomic, copy)NSString *EaseMobPassword;
@end

// 登录成功后返回用户model,需要为环信聊天窗口缓存用户信息
[ChatUserCacheUtil saveModel:user];

然后在接收环信消息的回调函数里保存用户信息,HsMainViewController.m是我们项目的主框架,我们在这里写了回调函数,无论群聊还是单聊消息,都会调用这里:

// 收到消息回调
-(void) didReceiveMessage:(EMMessage *)message
{
    [ChatUserCacheUtil saveDict:message.ext];

    BOOL needShowNotification = (message.messageType != eMessageTypeChat) ? [self needShowNotification:message.conversationChatter] : YES;
    if (needShowNotification) {
#if !TARGET_IPHONE_SIMULATOR

        BOOL isAppActivity = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
        if (!isAppActivity) {
            [self showNotificationWithMessage:message];
        }else {
            [self playSoundAndVibration];
        }
#endif
    }
}

环信页面主要是在ChatViewController和ConversationListViewController里显示用户对话,所以我们要在这两个页面里从缓存取用户头像和昵称,先从ChatViewController开始:

- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
                           modelForMessage:(EMMessage *)message
{
    id<IMessageModel> model = [[EaseMessageModel alloc] initWithMessage:message];

    ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.nickname];
    if (userinfo != nil) {
        model.nickname = userinfo.NickName;
        model.avatarURLPath = userinfo.AvatarUrl;
    }

    model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
    model.failImageName = @"imageDownloadFail";

    return model;
}

然后是ConversationListViewController:

#pragma mark - EaseConversationListViewControllerDataSource

- (id<IConversationModel>)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
                                    modelForConversation:(EMConversation *)conversation
{
    EaseConversationModel *model = [[EaseConversationModel alloc] initWithConversation:conversation];
    if (model.conversation.conversationType == eConversationTypeChat) {
        ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.conversation.chatter];
        if (userinfo != nil) {
            model.title = userinfo.NickName;
            model.avatarURLPath = userinfo.AvatarUrl;
        }
        model.avatarImage = PlaceholderImgChatUser;
    } else if (model.conversation.conversationType == eConversationTypeGroupChat) {
        // 此处省略100行代码........
    }
}

最后,为了让另外一个客户端也能正确显示头像和昵称,app发送消息时,要在消息扩展里附带用户信息,代码写在EaseSDKHelper.m里:

// 重新消息扩展组织
+(NSMutableDictionary*)reGetMessageExt:(NSDictionary *)messageExt{
    NSMutableDictionary *extDic = [NSMutableDictionary dictionaryWithDictionary:messageExt];
    [extDic setValue:[SettingData share].UserChatId forKey:kChatUserId];
    [extDic setValue:[SettingData share].UserHeadImg.ServerThumbUrlStr forKey:kChatUserPic];
    [extDic setValue:[SettingData share].UserName forKey:kChatUserNick];
    return extDic;
}

+ (EMMessage *)sendTextMessage:(NSString *)text
                            to:(NSString *)toUser
                   messageType:(EMMessageType)messageType
             requireEncryption:(BOOL)requireEncryption
                    messageExt:(NSDictionary *)messageExt

{
    // 表情映射。
    NSString *willSendText = [EaseConvertToCommonEmoticonsHelper convertToCommonEmoticons:text];
    EMChatText *textChat = [[EMChatText alloc] initWithText:willSendText];
    EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithChatObject:textChat];
    EMMessage *message = [[EMMessage alloc] initWithReceiver:toUser bodies:[NSArray arrayWithObject:body]];
    message.requireEncryption = requireEncryption;
    message.messageType = messageType;

    message.ext = [self reGetMessageExt:messageExt];
    EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
                                                                          progress:nil];

    return retMessage;
}


+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
                                      to:(NSString *)to
                             messageType:(EMMessageType)messageType
                       requireEncryption:(BOOL)requireEncryption
                              messageExt:(NSDictionary *)messageExt
                                progress:(id<IEMChatProgressDelegate>)progress
{
    return [self sendImageMessageWithImage:image to:to messageType:messageType requireEncryption:requireEncryption messageExt:messageExt quality:0.6 progress:progress];
}

+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
                                      to:(NSString *)to
                             messageType:(EMMessageType)messageType
                       requireEncryption:(BOOL)requireEncryption
                              messageExt:(NSDictionary *)messageExt
                                 quality:(float)quality
                                progress:(id<IEMChatProgressDelegate>)progress
{
    // 此处省略9行代码....

    message.ext = [self reGetMessageExt:messageExt];
    EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
                                                                          progress:progress];

    return retMessage;
}

+ (EMMessage *)sendVoiceMessageWithLocalPath:(NSString *)localPath
                                    duration:(NSInteger)duration
                                          to:(NSString *)to
                                 messageType:(EMMessageType)messageType
                           requireEncryption:(BOOL)requireEncryption
                                  messageExt:(NSDictionary *)messageExt
                                    progress:(id<IEMChatProgressDelegate>)progress
{
    // 此处省略4行代码....

    message.ext = [self reGetMessageExt:messageExt];
    EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
                                                                          progress:progress];

    return retMessage;
}

+ (EMMessage *)sendVideoMessageWithURL:(NSURL *)url
                                    to:(NSString *)to
                           messageType:(EMMessageType)messageType
                     requireEncryption:(BOOL)requireEncryption
                            messageExt:(NSDictionary *)messageExt
                              progress:(id<IEMChatProgressDelegate>)progress
{
    // 此处省略4行代码....

    message.ext = [self reGetMessageExt:messageExt];
    EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
                                                                          progress:progress];

    return retMessage;
}

+ (EMMessage *)sendFileMessage:(EMChatFile *)chatFile
                                          to:(NSString *)to
                                 messageType:(EMMessageType)messageType
                           requireEncryption:(BOOL)requireEncryption
                                  messageExt:(NSDictionary *)messageExt
                                    progress:(id<IEMChatProgressDelegate>)progress
{
    // 此处省略4行代码....

    message.ext = [self reGetMessageExt:messageExt];
    EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
                                                                          progress:progress];

    return retMessage;
}

以上代码为SDK V2版本,如果集成的是V3版本,请移步源码:

思路其实跟V2差不多,最大区别V3的回调方法didReceiveMessages比V2多了个【s】。

ChatUserCacheUtil我已经重命名为:UserCacheManager