开源中国的”弹一弹”中处理emoji的过程

emoji键盘

开源中国的emoji键盘类为EmojiPageVC,继承自UIPageViewController,为分页控制器。分页控制器的每个页面上的控制器类型为EmojiPanelVC,继承自UIViewController
EmojiPanelVC使用UICollectionView来布局emoji。EmojiPanelVC还有两个block回调:

  • void (^didSelectEmoji)(NSTextAttachment *textAttachment):表示的是点击某个emoji后的回调
  • void (^deleteEmoji)():表示的是删除某个emoji的回调

这里写图片描述

UICollectionView的数据源是一个名为emoji.plist的plist文件,只使用了其中的一部分。plist转为字典,key对应的的为名称字符串,value对应的为image的名称。如下:
这里写图片描述

点击UICollectionView的某一项之后的调用的结果如下:
//选中一个emoji的图片

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger section = indexPath.section;
    NSInteger row     = indexPath.row;

    if (section == 2 && row == 6) {
        //block回调,删除
        _deleteEmoji();
    } else {
        NSInteger emojiNum = _pageIndex * 20 + section * 7 + row + 1;
        NSString *emojiImageName, *emojiStr;
        if (emojiNum >= 106) {//emojiNum大于等于106的图片,图片名称类似为":finnadie:"
            emojiStr = Utils.emojiDict[@(emojiNum).stringValue];
            //去掉":"
            emojiImageName = [emojiStr stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@":"]];
        } else {//emojiNum小于106的图片,图片名称类似为"084"
            emojiStr = [NSString stringWithFormat:@"[%ld]", emojiNum - 1];
            emojiImageName = [NSString stringWithFormat:@"%03ld", emojiNum];
        }

        //创建Attachment
        NSTextAttachment *textAttachment = [NSTextAttachment new];
        textAttachment.image = [UIImage imageNamed:emojiImageName];
        [textAttachment adjustY:-3];//调整位置

        //给textAttachment关联一个对象
        objc_setAssociatedObject(textAttachment, @"emoji", emojiStr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

        //block回调,参数为NSTextAttachment
        _didSelectEmoji(textAttachment);
    }
}

EmojiPageVC中处理回调。例如我们输入”we”后,再输入一个emoji这里写图片描述。处理的过程如下:

        _didSelectEmoji = ^(NSTextAttachment *textAttachment) {//选择了一个emoji
            //通过NSTextAttachment创建emoji属性字符串
            NSAttributedString *emojiAttributedString = [NSAttributedString attributedStringWithAttachment:textAttachment];
            //通过textView的attributedText创建一个新的mutableAttributeString
            NSMutableAttributedString *mutableAttributeString = [[NSMutableAttributedString alloc] initWithAttributedString:textView.attributedText];
            //使用emojiAttributedString来替换
            [mutableAttributeString replaceCharactersInRange:textView.selectedRange withAttributedString:emojiAttributedString];
            //赋值给textView的attributedText
            textView.attributedText = mutableAttributeString;
            textView.textColor = [UIColor titleColor];
            [textView insertText:@""];
            textView.font = [UIFont systemFontOfSize:16];
        };

这里写图片描述

删除emoji:

        _deleteEmoji = ^ {//删除一个emoji
            [textView deleteBackward];
        };
上传emoji

发表一个”弹一弹”,在上传过程中如何处理emoji?
开源中国是这样处理的:

+ (NSString *)convertRichTextToRawText:(UITextView *)textView
{
    NSMutableString *rawText = [[NSMutableString alloc] initWithString:textView.text];

    //遍历其中的attachment
    [textView.attributedText enumerateAttribute:NSAttachmentAttributeName
                                        inRange:NSMakeRange(0, textView.attributedText.length)
                                        options:NSAttributedStringEnumerationReverse
                                     usingBlock:^(NSTextAttachment *attachment, NSRange range, BOOL *stop) {
                                                    if (!attachment) {return;}
                                                    //存在attachment,获取关联对象,即emojiStr,可能为[x]或者":xxxx:"
                                                    NSString *emojiStr = objc_getAssociatedObject(attachment, @"emoji");
                                                    //出入字符串
                                                    [rawText insertString:emojiStr atIndex:range.location];
                                                }];

    //匹配iOS系统的emoji
    NSString *pattern = @"[\ue000-\uf8ff]|[\\x{1f300}-\\x{1f7ff}]|\\x{263A}\\x{FE0F}|☺";
    NSError *error = nil;
    NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
    NSArray *resultsArray = [re matchesInString:textView.text options:0 range:NSMakeRange(0, textView.text.length)];

    //emoji转为text的字典
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *path = [bundle pathForResource:@"emojiToText" ofType:@"plist"];
    NSDictionary *emojiToText = [[NSDictionary alloc] initWithContentsOfFile:path];

    //把iOS系统的emoji替换为文字,例如":grinning::sweat_smile:"
    for (NSTextCheckingResult *match in [resultsArray reverseObjectEnumerator]) {
        NSString *emoji = [textView.text substringWithRange:match.range];
        [rawText replaceCharactersInRange:match.range withString:emojiToText[emoji]];
    }

    return [rawText stringByReplacingOccurrencesOfString:@"\U0000fffc" withString:@""];
}

举个例子说明,我们输入以下的内容:
这里写图片描述
前两个emoji为iOS系统的emoji,后面连个是自定义emoji键盘的。

最看是rawText的内容为:
这里写图片描述

textView.attributedText遍历其中的NSAttachmentAttributeName之后,rawText的内容为:
这里写图片描述

需要注意的是这里写图片描述,在emoji.plist文件中是这样定义的:
这里写图片描述

然后是替换,系统的emoji,之后rawText内容为:
这里写图片描述

网络获取emoji之后的显示

发表”弹一弹”之后,在cell中显示动弹。网络获取的内容为:

aa:grinning::sweat_smile:[][]

这时需要把字符串替换为emoji

+ (NSAttributedString *)emojiStringFromRawString:(NSString *)rawString
{
    //创建属性字符处
    NSMutableAttributedString *emojiString = [[NSMutableAttributedString alloc] initWithString:rawString];
    //emoji字典
    NSDictionary *emoji = self.emojiDict;

    //匹配emoji unicode编码[\u4e00-\u9fa5]匹配所有中文
    NSString *pattern = @"\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]|:[a-zA-Z0-9\\u4e00-\\u9fa5_]+:";
    NSError *error = nil;
    NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];

    NSArray *resultsArray = [re matchesInString:rawString options:0 range:NSMakeRange(0, rawString.length)];

    NSMutableArray *emojiArray = [NSMutableArray arrayWithCapacity:resultsArray.count];

    for (NSTextCheckingResult *match in resultsArray) {
        NSRange range = [match range];
        NSString *emojiName = [rawString substringWithRange:range];

        if ([emojiName hasPrefix:@"["] && emoji[emojiName]) {//自定义的emoji,带"["的
            //创建attachment 添加图片
            NSTextAttachment *textAttachment = [NSTextAttachment new];
            textAttachment.image = [UIImage imageNamed:emoji[emojiName]];
            //调整位置
            [textAttachment adjustY:-3];

            //创建attribute string
            NSAttributedString *emojiAttributedString = [NSAttributedString attributedStringWithAttachment:textAttachment];

            //创建字典,,添加到emojiArray中
            [emojiArray addObject: @{@"image": emojiAttributedString, @"range": [NSValue valueWithRange:range]}];
        } else if ([emojiName hasPrefix:@":"]) {//以":"开头的
            if (emoji[emojiName]) {//emoji字典中存在的
                [emojiArray addObject:@{@"text": emoji[emojiName], @"range": [NSValue valueWithRange:range]}];
            } else {//emoji字典中不存在的
                UIImage *emojiImage = [UIImage imageNamed:[emojiName stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@":"]]];
                NSTextAttachment *textAttachment = [NSTextAttachment new];
                textAttachment.image = emojiImage;
                [textAttachment adjustY:-3];

                NSAttributedString *emojiAttributedString = [NSAttributedString attributedStringWithAttachment:textAttachment];

                [emojiArray addObject: @{@"image": emojiAttributedString, @"range": [NSValue valueWithRange:range]}];
            }
        }
    }

    //遍历emojiArray
    for (NSInteger i = emojiArray.count -1; i >= 0; i--) {
        NSRange range;
        [emojiArray[i][@"range"] getValue:&range];//获取range
        //替换range中的内容
        if (emojiArray[i][@"image"]) {
            [emojiString replaceCharactersInRange:range withAttributedString:emojiArray[i][@"image"]];
        } else {
            [emojiString replaceCharactersInRange:range withString:emojiArray[i][@"text"]];
        }
    }

    return emojiString;
}

处理后的结果为:
这里写图片描述

Coding iOS客户端”发冒泡”处理emoji过程

emoji键盘

Coding的emoji键盘使用的是AGEmojiKeyboardView类,由AGEmojiKeyboard改变而来。支持普通的emoji,也支持大图Monkey和Monkey_Gif(自定义的)。如下:
这里写图片描述这里写图片描述这里写图片描述

点击一个emoji之后,调用的代理方法- (void)emojiKeyBoardView:(AGEmojiKeyboardView *)emojiKeyBoardView didUseEmoji:(NSString *)emoji

- (void)emojiKeyBoardView:(AGEmojiKeyboardView *)emojiKeyBoardView didUseEmoji:(NSString *)emoji {
    NSRange selectedRange = self.tweetContentView.selectedRange;

    NSString *emotion_monkey = [emoji emotionMonkeyName];
    if (emotion_monkey) {//monkey类emoji表情
        emotion_monkey = [NSString stringWithFormat:@" :%@: ", emotion_monkey];
        self.tweetContentView.text = [self.tweetContentView.text stringByReplacingCharactersInRange:selectedRange withString:emotion_monkey];
        self.tweetContentView.selectedRange = NSMakeRange(selectedRange.location +emotion_monkey.length, 0);
        [self textViewDidChange:self.tweetContentView];
    }else{//非monkey类emoji表情
        self.tweetContentView.text = [self.tweetContentView.text stringByReplacingCharactersInRange:selectedRange withString:emoji];
        self.tweetContentView.selectedRange = NSMakeRange(selectedRange.location +emoji.length, 0);
        [self textViewDidChange:self.tweetContentView];
    }
}

输入如下的emoji,这里写图片描述这里写图片描述这里写图片描述,之后,textView的文本内容是:
这里写图片描述

上传emoji

上传emoji的时候,需要把emoji转为字符。Coding使用的是开源项目NSString-Emoji,在其基础上加了一些内容。转后的结果为:

:smiley: :哈哈:  :奔月: 

基本逻辑是遍历字符串,然后替换字符串:

    [text enumerateSubstringsInRange:NSMakeRange(0, text.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        if (self.aliaseForEmojis[substring]) {
            NSString *aliase = self.aliaseForEmojis[substring];
            resultText = [resultText stringByReplacingOccurrencesOfString:substring withString:aliase];
        }
    }];
把字符串转为emoji

貌似的是Coding在后台给处理了。如下
这里写图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐