【OC-环信】环信3.x自定义表情实现

首语

最近一直在搞一个 2015 年的项目,需要把对应项目中的环信 2.x 升级到 环信 3.x,而在本项目中使用的环信 SDK 是 3.4.1 版本的。
图片-环信 Hyphenate 3.4.1

而项目中的需求之一就是自定义表情,不使用苹果系统的 Emoji 表情。同时也查了网上的相关资料,发现还是比较少的,所以打算在此记录一下相关笔记。

自定义表情的实质

自定义表情,实际上也是通过发送文本消息一样发送出去,只不过是对文本进行了特殊处理——编码转换。

常见的自定义表情,一般都是提供一组表情素材图片,然后通过编码,将表情素材图片与对应的编码进行一一绑定。无论发送消息还是接收消息时,实际上都是文本消息。通过在本地客户的对消息进行处理,对于发出去的消息,将图片通过一一对应的编码转成文本字符发出去;对于接收的消息,将接收的文本字符转成一一对应的图片进行显示。

自定义表情实现

1、对自定义表情素材图片进行编码转码绑定

① 先导入对应要展示的自定义表情素材图片。
图片 - 自定义表情素材图片导入

② 找到 EaseUI/EMUIKit/Util/ 目录下的 EaseConvertToCommonEmoticonsHelper 类,该类主要是处理表情编码的。该类已经默认处理了系统 Emoji 表情的编码转码工作。
图片 - EaseConvertToCommonEmoticonsHelper 处理的系统 Emoji 表情编码转码方法

③ 对该类进行方法扩展,扩展的方法主要是实现自己自定义表情的编码转码工作。

注意:如果不想侵入 EaseUI 的源码,可以选择通过 Category 扩展的方式进行添加方法。

#pragma mark - EaseConvertToCommonEmoticonsHelper.h

// 自定义表情编码转码
+ (NSDictionary *)emotionsDictionary;
+ (NSArray*)emotionsArray;
#pragma mark - EaseConvertToCommonEmoticonsHelper.m


+ (NSArray*)emotionsArray {

    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"[爱你]",@"[奥特曼]",@"[拜拜]",@"[悲伤]",@"[鄙视]",@"[闭嘴]",@"[馋嘴]",@"[吃惊]",@"[打哈欠]",@"[叮]", @"[愤怒]",@"[感冒]",@"[鼓掌]",@"[哈哈]",@"[害羞]",@"[汗]",@"[呵呵]",@"[无语]",@"[哼]",@"[花心]",@"[可爱]",@"[可怜]",@"[酷]",@"[困]",@"[傲慢]", @"[累]",@"[男孩儿]",@"[怒]",@"[怒骂]",@"[女孩儿]",@"[钱]",@"[亲亲]",@"[生病]",@"[失望]",@"[衰]",@"[书呆子]",@"[睡觉]",@"[思考]",@"[太开心]", @"[丢笑]",@"[吐]",@"[兔子]",@"[抠鼻]",@"[委屈]",@"[熊猫]",@"[嘻嘻]",@"[嘘]",@"[阴险]",@"[疑问]",@"[右哼哼]",@"[晕]",@"[抓狂]",@"[猪头]", @"[左怪脸]",@"[左哼哼]",@"[给力]",@"[互粉]",@"[囧]",@"[萌]",@"[神马]",@"[帅]",@"[V5]",@"[囍]",@"[织]",@"[不要]",@"[肥皂]",@"[good]",@"[来]",@"[OK]",@"[拳头]",@"[弱]",@"[握手]",@"[胜利]",@"[赞]",@"[最差]",@"[爱心]",@"[伤心]",@"[冰棍]",@"[蛋糕]",@"[飞机]",@"[风扇]",@"[啤酒]",@"[红领带]",@"[话筒]",@"[咖啡]",@"[蜡烛]",@"[礼物]",@"[绿领带]",@"[汽车]",@"[实习]",@"[手机]",@"[手套]",@"[网球]",@"[围脖]",@"[围观]",@"[帽子]",@"[西瓜]",@"[音乐]",@"[照相]",@"[钟表]",@"[自行车]",@"[足球]",@"[乌云]",@"[落叶]",@"[沙尘暴]",@"[太阳]",@"[微风]",@"[鲜花]", nil];

    return array;
}

+ (NSDictionary *)emotionsDictionary {

    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"[爱你]",@"[奥特曼]",@"[拜拜]",@"[悲伤]",@"[鄙视]",@"[闭嘴]",@"[馋嘴]",@"[吃惊]",@"[打哈欠]",@"[叮]", @"[愤怒]",@"[感冒]",@"[鼓掌]",@"[哈哈]",@"[害羞]",@"[汗]",@"[呵呵]",@"[无语]",@"[哼]",@"[花心]",@"[可爱]",@"[可怜]",@"[酷]",@"[困]",@"[傲慢]", @"[累]",@"[男孩儿]",@"[怒]",@"[怒骂]",@"[女孩儿]",@"[钱]",@"[亲亲]",@"[生病]",@"[失望]",@"[衰]",@"[书呆子]",@"[睡觉]",@"[思考]",@"[太开心]", @"[丢笑]",@"[吐]",@"[兔子]",@"[抠鼻]",@"[委屈]",@"[熊猫]",@"[嘻嘻]",@"[嘘]",@"[阴险]",@"[疑问]",@"[右哼哼]",@"[晕]",@"[抓狂]",@"[猪头]", @"[左怪脸]",@"[左哼哼]",@"[给力]",@"[互粉]",@"[囧]",@"[萌]",@"[神马]",@"[帅]",@"[V5]",@"[囍]",@"[织]",@"[不要]",@"[肥皂]",@"[good]",@"[来]",@"[OK]",@"[拳头]",@"[弱]",@"[握手]",@"[胜利]",@"[赞]",@"[最差]",@"[爱心]",@"[伤心]",@"[冰棍]",@"[蛋糕]",@"[飞机]",@"[风扇]",@"[啤酒]",@"[红领带]",@"[话筒]",@"[咖啡]",@"[蜡烛]",@"[礼物]",@"[绿领带]",@"[汽车]",@"[实习]",@"[手机]",@"[手套]",@"[网球]",@"[围脖]",@"[围观]",@"[帽子]",@"[西瓜]",@"[音乐]",@"[照相]",@"[钟表]",@"[自行车]",@"[足球]",@"[乌云]",@"[落叶]",@"[沙尘暴]",@"[太阳]",@"[微风]",@"[鲜花]", nil];

    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    for (int i=0; i<array.count; i++) {
        [dic setObject:[NSString stringWithFormat:@"%03d", i+1] forKey:array[i]];
    }

    return dic;
}

图片 - EaseConvertToCommonEmoticonsHelper.h 扩展自定义表情编码转码方法

图片 - EaseConvertToCommonEmoticonsHelper.m 扩展自定义表情编码转码方法

2、在 didFinishLaunchingWithOptions 中初始化设置自定义表情相关代码

环信社区中给的参考资料说是尽量放在 HelpDeskUI 中,但是我发现在 环信 3.4.1 中并没有这个类文件,所以就选择了放在 didFinishLaunchingWithOptions 中进行初始化设置,从而保证使用。

  • 调用了 EaseEmotionEscape 类中的两个方法:
    • - (void) setEaseEmotionEscapePattern:(NSString*)pattern;
    • - (void) setEaseEmotionEscapeDictionary:(NSDictionary*)dict;
  • 这两个方法是保证在聊天发送表情消息时,输入框中显示的是对应的表情,而不是对应的编码字符。必须要设置的。
#pragma mark - AppDelegate.m


[[EaseEmotionEscape sharedInstance] setEaseEmotionEscapePattern:@"\\[[^\\[\\]]{1,3}\\]"];
[[EaseEmotionEscape sharedInstance] setEaseEmotionEscapeDictionary:[EaseConvertToCommonEmoticonsHelper emotionsDictionary]];

图片 - 初始化设置自定义表情相关代码

3、设置聊天界面自定义表情相关代码

在聊天界面,如果需要自定义表情,则需要重写 EaseMessageViewController 类的关于表情的方法:

  • - (NSArray*)emotionFormessageViewController:(EaseMessageViewController *)viewController;
  • 该方法属于 EaseMessageViewControllerDataSource 的代理方法;
    • @property (weak, nonatomic) id<EaseMessageViewControllerDataSource> dataSource;
  • 所以需要记得实现绑定代理协议,否则重写的 emotionFormessageViewController 方法会不执行的:
    • self.dataSource = self;
/** 替换表情 **/
- (NSArray*)emotionFormessageViewController:(EaseMessageViewController *)viewController {
    /** 系统表情(此处本来应该是隐藏的,为了方便看系统表情实现,所以代码暂时未屏蔽,此种情况下显示的,应该是两个表情容器;如果不需要使用系统表情容器,这段代码直接屏蔽即可![](https://images.xiaozhuanlan.com/photo/2018/08bab5caa05e59f66f3fd5c4e74f969b.png)
[](https://images.xiaozhuanlan.com/photo/2018/62e3bf4d9164e37f9a20c67532d16534.jpg)
 **/
    NSMutableArray *emotions = [NSMutableArray array];
    for (NSString *name in [EaseEmoji allEmoji]) {
        EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name emotionThumbnail:name emotionOriginal:name emotionOriginalURL:@"" emotionType:EMEmotionDefault];
        [emotions addObject:emotion];
    }
    EaseEmotion *temp = [emotions objectAtIndex:0];
    EaseEmotionManager *managerDefault = [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:emotions tagImage:[UIImage imageNamed:temp.emotionId]];


    /** 自定义表情 **/
    NSMutableArray *emotions2 = [NSMutableArray array];
    NSMutableArray *nameArr2 = [NSMutableArray arrayWithCapacity:0];
    NSString *name2 = nil;
    for (int i=1; i<=[[EaseConvertToCommonEmoticonsHelper emotionsArray] count]; i++) {
        name2 = [NSString stringWithFormat:@"%03d",i];
        [nameArr2 addObject:name2];
    }
    int j=0;
    for (NSString *name2 in [EaseConvertToCommonEmoticonsHelper emotionsArray]) {
        EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name2 emotionThumbnail:nameArr2[j] emotionOriginal:nameArr2[j] emotionOriginalURL:@"" emotionType:EMEmotionPng];
        [emotions2 addObject:emotion];
        j++;
    }
    EaseEmotion *temp2 = [emotions2 objectAtIndex:0];
    EaseEmotionManager *managerDefault2 = [[EaseEmotionManager alloc] initWithType:EMEmotionPng emotionRow:3 emotionCol:7 emotions:emotions2 tagImage:[UIImage imageNamed:temp2.emotionThumbnail]];

    return, managerDefault2];
}

图片 - 重写 emotionFormessageViewController 方法

4、调整自定义表情容器显示大小

如果需要调整自定义表情容器的显示,则需要找到对应的视图 View。而此处我们则需要找到 EaseUI/EMUIKit/Views/conversation/toolbar/FaceView 目录下的 EaseFacialView 类文件,就是在此处进行调整的。

通过此处的代码我们可以了解到,该自定义表情容器 View 是一个 UICollectionView,且是纵向的,即数据源不是从左向右依次的,而是从上到下,再从左到右依次进行展现的。

此处我们可以对以下涉及位置的 UICollectionView 代理方法进行调整:

  • 设置表情尺寸大小:
    • - (CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
  • 设置表情容器到边缘距离(会影响到表情间距):
    • - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
  • 设置表情行与行之间的间距(注意此时是纵向添加的,所以行与行间距应该是横向的):
    • - (CGFloat)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
  • 设置表情与表情之间的间距(注意此时是纵向添加的,所以表情与表情的间距应该是纵向的):
    • - (CGFloat)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

此处我这边设置的是表情与表情间间距为 0,行与行之间的间距 15,距离边缘距离顶部、底部各为 5,左侧右侧各为 7.5,而这个左侧右侧的间距 7.5 正好取的是行与行之间间距的一半,防止按页滚动的时候发生偏移。

#pragma mark - EaseFacialView.m


/** 设置表情尺寸大小 **/
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    EaseEmotionManager *emotionManager = [_emotionManagers objectAtIndex:indexPath.section];
    NSInteger maxRow = emotionManager.emotionRow;
    NSInteger maxCol = emotionManager.emotionCol;
    CGFloat itemWidth = self.frame.size.width / maxCol;
    CGFloat itemHeight = (self.frame.size.height) / maxRow;

    float width = (CGRectGetWidth(self.frame)-7.5*2-(emotionManager.emotionCol-1)*15)/emotionManager.emotionCol;
    if (emotionManager.emotionType == EMEmotionPng) {
        return CGSizeMake(width,width);
    }
    return CGSizeMake(itemWidth, itemHeight);
}

/** 设置表情容器到边缘距离(会影响到表情间距) **/
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {

    EaseEmotionManager *emotionManager = [_emotionManagers objectAtIndex:section];
    if (emotionManager.emotionType == EMEmotionPng) {
        return UIEdgeInsetsMake(5.f, 7.5f, 5.f, 7.5f);
    }
    return UIEdgeInsetsMake(0, 0, 0, 0);
}

/** 设置表情行与行之间的间距(注意此时是纵向添加的,所以行与行间距应该是横向的) **/
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {

    EaseEmotionManager *emotionManager = [_emotionManagers objectAtIndex:section];
    if (emotionManager.emotionType == EMEmotionPng) {
        return 15.f;
    }

    return 0.0f;
}

/** 设置表情与表情之间的间距(注意此时是纵向添加的,所以表情与表情的间距应该是纵向的) **/
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {

    EaseEmotionManager *emotionManager = [_emotionManagers objectAtIndex:section];
    if (emotionManager.emotionType == EMEmotionPng) {
        return 0.f;
    }

    return 0.0f;
}

图片 - 调整自定义表情容器显示大小(上)

图片 - 调整自定义表情容器显示大小(下)

至此,自定义表情完成!!!瞅瞅效果吧:

图片 - 环信 3.4.1 自定义表情最终效果

参考链接

© 著作权归作者所有
这个作品真棒,我要支持一下!
该专栏主要用于记录日常iOS开发中的笔记心得,包括 Objective-C、Swift、Mac OS 开发及学习随笔。
0条评论
top Created with Sketch.