[iOS] 自定義表情包

一.前言

平常我們使用的表情包大多是emoji表情,而到了微信和qq上我們會(huì)見到更多更豐富的表情,那么這些表情是如何做的呢,下面就讓我來帶你們來揭曉答案。

注: 方法都來自互聯(lián)網(wǎng), 在此鳴謝走在前面鋪路的前輩們.

二.準(zhǔn)備階段

準(zhǔn)備一個(gè)表情包 就是一組表情如圖1所示.

圖1

我這里借用了一下騰訊的表情包 一共是50個(gè)表情

三.編寫階段, 這里只說明核心功能, UI簡略說明.

1.效果圖(you say a jb without a picture) 如圖2所示.

圖2

2.先畫一個(gè)表情鍵盤

從效果圖上可以看出來 下面的就是表情鍵盤(非常簡陋! 我想吐) 咳咳... 如圖3所示.

圖3

這個(gè)表情鍵盤我是用collectionView畫的(這個(gè)過程略), 使用過這個(gè)控件的人都知道, 我們還需要一個(gè)數(shù)據(jù)源來表示那些小表情, 接下來就是創(chuàng)建數(shù)據(jù)源的過程:

- (NSMutableArray *)faceArray {
    if (!_faceArray) {
        _faceArray = [NSMutableArray array];
        
        //創(chuàng)建一個(gè)標(biāo)簽數(shù)組
        NSArray *tagArr = @[@"[齜牙]", @"[吐舌]", @"[流汗]", @"[偷笑]", @"[再見]", @"[砸]", @"[擦汗]", @"[豬頭]", @"[玫瑰]", @"[流淚]", @"[大哭]", @"[噓]", @"[酷]", @"[抓狂]", @"[委屈]", @"[便便]", @"[地雷]", @"[菜刀]", @"[可愛]", @"[心心眼]", @"[害羞]", @"[帥氣]", @"[吐]", @"[笑臉]", @"[生氣]", @"[尷尬]", @"[驚嚇]", @"[尷尬2]", @"[心]", @"[嘴唇]", @"[白眼]", @"[傲慢]", @"[難過]", @"[驚訝]", @"[疑問]", @"[睡覺]", @"[親親]", @"[憨笑]", @"[企鵝愛]", @"[衰]", @"[撇嘴]", @"[陰險(xiǎn)]", @"[加油]", @"[發(fā)呆]", @"[睡著]", @"[抱抱]", @"[壞笑]", @"[飛吻]", @"[鄙視]", @"[暈]"];
        
        for (NSInteger i = 1; i < 51; i++) {
            //創(chuàng)建一個(gè)表情對(duì)象
            FaceAttachment *model = [[FaceAttachment alloc] init];
            //從第一張圖片依次賦值
            model.imageName = [NSString stringWithFormat:@"%03ld", i];
            //刻上標(biāo)簽名 用途:上傳服務(wù)器替換
            model.tagName = tagArr[i - 1];
            //將model裝入數(shù)組
            [_faceArray addObject:model];
        }
    }
    return _faceArray;
}

下面是FaceAttachment
.h

#import <UIKit/UIKit.h>

@interface FaceAttachment : NSTextAttachment
@property(nonatomic, strong) NSString *imageName; /** 表情圖片名 */
@property(nonatomic, strong) NSString *tagName; /** 標(biāo)簽名 */
@property(nonatomic, assign) NSRange range; /** 位置 */
@end

.m

#import "FaceAttachment.h"

@implementation FaceAttachment
- (UIImage *)image {
    return [UIImage imageNamed:_imageName];
}
@end

FaceAttachment的對(duì)象就是每個(gè)小表情, 這個(gè)model中有三個(gè)屬性

  1. imageName 圖片名 其實(shí)我們看到的表情就是一個(gè)個(gè)的小圖片
  2. tagName 標(biāo)簽名 上傳服務(wù)器時(shí)要將這些富文本中的表情 替換成普通的字符串標(biāo)簽才能上傳
  3. range 記錄表情的位置

3.控制鍵盤彈出邏輯

到這里有人會(huì)提出一個(gè)問題, 切換表情鍵盤后怎么讓光標(biāo)存在?
這個(gè)問題其實(shí)很簡單, 只需要把表情鍵盤設(shè)置為textView.inputView就行了, 使用過程是先設(shè)置好inputView再彈出鍵盤, 不然inputView是不會(huì)自動(dòng)切換的, 如果在鍵盤彈出狀態(tài)下切換inputView也很簡單, 方法就是先回收鍵盤 -> 切換inputView -> 再彈出即可 下面是表情與鍵盤的切換代碼

- (IBAction)faceButton:(id)sender {
    
    if (!_keyBoardFlag) {
        
        [self.view endEditing:YES];
        self.textView.inputView = self.faceBoard;
        [self.textView becomeFirstResponder];
        [self.faceButton setImage:[UIImage imageNamed:@"鍵盤"] forState:UIControlStateNormal];
    }
    
    else {
        
        [self.view endEditing:YES];
        self.textView.inputView = nil;
        [self.textView becomeFirstResponder];
        [self.faceButton setImage:[UIImage imageNamed:@"表情"] forState:UIControlStateNormal];
    }
    
    _keyBoardFlag = !_keyBoardFlag;
}

4.插入表情的核心代碼

其實(shí)原理很簡單就是利用富文本進(jìn)行一個(gè)表情的拼接 代碼如下

+ (void)insertFaceToString:(FaceAttachment *)model textView:(UITextView *)textView {
    
    //創(chuàng)建一個(gè)附件
    FaceAttachment *faceAttachement = [[FaceAttachment alloc]init];
    //添加表情
    faceAttachement.imageName = model.imageName;
    //添加標(biāo)簽名
    faceAttachement.tagName = model.tagName;
    
    //設(shè)置表情大小
    faceAttachement.bounds = CGRectMake(0, 0, 18, 18);
    //記錄光標(biāo)位置
    NSInteger location = textView.selectedRange.location;
    //插入表情
    [textView.textStorage insertAttributedString:[NSAttributedString attributedStringWithAttachment:faceAttachement] atIndex:textView.selectedRange.location];
    //將光標(biāo)位置向前移動(dòng)一個(gè)單位
    textView.selectedRange = NSMakeRange(location + 1, 0);
}

其中 FaceAttachment 是繼承于 NSTextAttachment 這個(gè)東西就相當(dāng)于一個(gè)相框 用它裝表情圖片 然后拼接到文本上去, 為什么要繼承 NSTextAttachment ? 因?yàn)槲覀円娣诺臇|西在它原有基礎(chǔ)上是不夠的, 所以要繼承 寫上去三個(gè)屬性

FaceAttachment的對(duì)象就是每個(gè)小表情, 這個(gè)model中有三個(gè)屬性

  1. imageName 圖片名 其實(shí)我們看到的表情就是一個(gè)個(gè)的小圖片
  2. tagName 標(biāo)簽名 上傳服務(wù)器時(shí)要將這些富文本中的表情 替換成普通的字符串標(biāo)簽才能上傳
  3. range 記錄表情的位置

5.上傳服務(wù)器

通過上面的步驟 我想你們應(yīng)該對(duì)表情是如何寫在文本上有一個(gè)了解了 但是我要說的是 這樣的表情是不能上傳到服務(wù)器的, 所以我們要把它們轉(zhuǎn)化成普通的純文本字符才能去上傳 下面的代碼就是把這些富文本表情轉(zhuǎn)化成純文本字符.
轉(zhuǎn)化前效果圖:

圖4

轉(zhuǎn)化后文字:

請(qǐng)輸入文字[齜牙][齜牙][齜牙]

下面是轉(zhuǎn)化代碼:

- (NSString *)toString {
    
    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithAttributedString:self];
    
    __block NSUInteger index = 0;
    
    [self enumerateAttribute:NSAttachmentAttributeName inRange:NSMakeRange(0, self.length) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
        
        //從富文本中遍歷出 FaceAttachment 對(duì)象
        if (value && [value isKindOfClass:[FaceAttachment class]]) {
            FaceAttachment *faceAttachment = value;
            //替換對(duì)象為[表情]
            [attributeString replaceCharactersInRange:NSMakeRange(range.location + index, range.length) withString:faceAttachment.tagName];
            //替換后對(duì)位置作一下調(diào)整(因?yàn)樘鎿Q前長度為1替換后有可能是4 [齜牙] 也有可能是3 [暈])
            index += faceAttachment.tagName.length - 1;
        }
    }];
    
    return attributeString.string;
}

上面的代碼自己寫寫 很簡單的.

6.服務(wù)器上獲取的純文本轉(zhuǎn)化為帶表情的富文本

通過上面的步驟 我們已經(jīng)可以把表情和文字都轉(zhuǎn)化成純文本的形式上傳的服務(wù)器上了, 接下來我們要做的就是把服務(wù)器上獲取的這些字符串再轉(zhuǎn)換回表情 其實(shí)中心思想就是用正則表達(dá)式過濾出"[表情]"這樣的標(biāo)簽并記錄他們的位置, 然后創(chuàng)建出 FaceAttachment 對(duì)象 對(duì) "[表情]" 這樣的標(biāo)簽來進(jìn)行替換

- (NSAttributedString *)faceWithServerString:(NSString *)string {
    
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:string];
    
    NSString *pattern = @"\\[[^\\[|^\\]]+\\]";
    NSError *error = nil;
    NSRegularExpression *regularExpression = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
    if (!regularExpression) {
        NSLog(@"錯(cuò)誤信息 : %@", error);
    }
    
    //將匹配到的字符存入數(shù)組
    NSArray *resultArr = [regularExpression matchesInString:string options:0 range:NSMakeRange(0, string.length)];
    
    NSMutableArray *faceModelArr = [NSMutableArray array];
    
    for (NSTextCheckingResult *result in resultArr) {
        NSRange range = result.range;
        NSString *subString = [string substringWithRange:range];
        
        for (FaceAttachment *model in self.faceArray) {
            if ([subString isEqualToString:model.tagName]) {
                
                FaceAttachment *faceAttachment = [[FaceAttachment alloc]init];
                faceAttachment.imageName = model.imageName;
                faceAttachment.tagName = model.tagName;
                faceAttachment.range = range;
                faceAttachment.bounds = CGRectMake(0, 0, 18, 18);
                
                [faceModelArr addObject:faceAttachment];
            }
        }
    }
    
    faceModelArr = [NSMutableArray arrayWithArray:[[faceModelArr reverseObjectEnumerator] allObjects]];
    
    for (FaceAttachment *faceAttachment in faceModelArr) {
        NSAttributedString *faceAttributedString = [NSAttributedString attributedStringWithAttachment:faceAttachment];
        [attributedString replaceCharactersInRange:faceAttachment.range withAttributedString:faceAttributedString];
    }
    
    
    [attributedString addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(0, attributedString.length)];
    
    return attributedString;
}

四.Demo

下面是本人寫的源碼, 可以做為參考, 有不對(duì)的地方還請(qǐng)指教!
https://github.com/iwgo/CustomFace.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容