一.前言
平常我們使用的表情包大多是emoji表情,而到了微信和qq上我們會(huì)見到更多更豐富的表情,那么這些表情是如何做的呢,下面就讓我來帶你們來揭曉答案。
注: 方法都來自互聯(lián)網(wǎng), 在此鳴謝走在前面鋪路的前輩們.
二.準(zhǔn)備階段
準(zhǔn)備一個(gè)表情包 就是一組表情如圖1所示.
我這里借用了一下騰訊的表情包 一共是50個(gè)表情
三.編寫階段, 這里只說明核心功能, UI簡略說明.
1.效果圖(you say a jb without a picture) 如圖2所示.
2.先畫一個(gè)表情鍵盤
從效果圖上可以看出來 下面的就是表情鍵盤(非常簡陋! 我想吐) 咳咳... 如圖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è)屬性
- imageName 圖片名 其實(shí)我們看到的表情就是一個(gè)個(gè)的小圖片
- tagName 標(biāo)簽名 上傳服務(wù)器時(shí)要將這些富文本中的表情 替換成普通的字符串標(biāo)簽才能上傳
- 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è)屬性
- imageName 圖片名 其實(shí)我們看到的表情就是一個(gè)個(gè)的小圖片
- tagName 標(biāo)簽名 上傳服務(wù)器時(shí)要將這些富文本中的表情 替換成普通的字符串標(biāo)簽才能上傳
- range 記錄表情的位置
5.上傳服務(wù)器
通過上面的步驟 我想你們應(yīng)該對(duì)表情是如何寫在文本上有一個(gè)了解了 但是我要說的是 這樣的表情是不能上傳到服務(wù)器的, 所以我們要把它們轉(zhuǎn)化成普通的純文本字符才能去上傳 下面的代碼就是把這些富文本表情轉(zhuǎn)化成純文本字符.
轉(zhuǎn)化前效果圖:
轉(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