怎么在iOS中实现表情键盘?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
基本思路
首先,表情包的图片是用bundle的形式组织的,用PPSticker类表征一套表情包,用PPEmoji类表征某一个表情,用一个plist作为配置文件,存储表情包的信息。
表情的组织.jpg
PPStickerDataManager类主要负责数据部分,用单例的形式,这样可以在初始化的时候只会读取一次plist文件中的所有表情信息;同时我们把输入框内容发到服务端以及从服务端请求到的都是纯文本的,比如会把 "笑死了?" 转成 "笑死了[笑哭]" 这样的纯文本,而不是直接把表情图片直接发到服务端,也就是说项目中有大量的地方会有把文本->表情的操作,所以PPStickerDataManager类也提供匹配某段纯文本中的表情,并把文本替换为图片的功能,PPStickerDataManager类的头文件如下:
@interface PPStickerDataManager : NSObject + (instancetype)sharedInstance; /// 所有的表情包 @property (nonatomic, strong, readonly) NSArray<PPSticker *> *allStickers; /* 匹配给定attributedString中的所有emoji,如果匹配到的emoji有本地图片的话会直接换成本地的图片 * * @param attributedString 可能包含表情包的attributedString * @param font 表情图片的对齐字体大小 */ - (void)replaceEmojiForAttributedString:(NSMutableAttributedString *)attributedString font:(UIFont *)font; @end
"真正的"键盘
真正的键盘也就是说调起表情键盘时输入框是有光标的,能进行拖拽光标、选中区域等的操作,这样的体验才是与系统键盘一致的。其实系统已经提供好了接口给我们直接使用,UITextView和UITextField都有的inputView和inputAccessoryView就是用来实现自定义键盘的,这两个属性的定义如下:
// Presented when object becomes first responder. If set to nil, reverts to following responder chain. If // set while first responder, will not take effect until reloadInputViews is called. @property (nullable, readwrite, strong) UIView *inputView; @property (nullable, readwrite, strong) UIView *inputAccessoryView;
同时系统键盘在 设置->声音->按键音 选项打开且手机非静音状态下输入是有按键的声音的,这个按键音也是可以支持的,只要自定义键盘类遵循UIInputViewAudioFeedback协议,同时实现 enableInputClicksWhenVisible方法并返回YES,这样就可以在点击表情的时候调用[[UIDevice currentDevice] playInputClick]方法发出按键音了,详情请查看苹果的官方文档。
下面是Demo中键盘切换方法的实现:
- (void)changeKeyboardTo:(PPKeyboardType)toType { switch (toType) { case PPKeyboardTypeSystem: self.textView.inputView = nil; // 切换到系统键盘 [self.textView reloadInputViews]; // 调用reloadInputViews方法会立刻进行键盘的切换 break; case PPKeyboardTypeSticker: self.textView.inputView = self.stickerKeyboard; // 切换到自定义的表情键盘 [self.textView reloadInputViews]; break; default: break; } }
去除表情的拖拽交互
在iOS11上,UITextView上的NSTextAttachment(表情)默认可以进行拖拽交互,但是却导致拖动光标时很容易触发这个交互(图示可以查看上面说到的微博国际版中的误触)。一番查找之后才找到一个比较隐蔽的属性:textDragInteraction,直接设置为NO就能禁止掉NSTextAttachment的拖拽交互。
if (@available(iOS 11.0, *)) { // 只在iOS11及以上才有这个属性 _textView.textDragInteraction.enabled = NO; }
与服务端的交互
我们在输入框中输入的内容与服务端进行交互的时候都是用纯文本的,比如会把 "笑死了?" 转成 "笑死了[笑哭]" 这样的纯文本发到服务端,而不是直接发表情图片,向服务端请求内容的时候也是传回 "笑死了[笑哭]",然后客户端再根据正则匹配找出表情替换成对应的表情图片,然后显示到页面上。具体过程可以看下图:
与服务端的交互.png
也就是说,我们设置到输入框的NSAttributedString中的每一个NSTextAttachment都有一个"隐藏的"属性—表情的文本描述,这里对NSAttributedString进行拓展就能实现。pp_setTextBackedString可以对NSAttributedString的指定range设置一个PPTextBackedString类型的属性,而pp_plainTextForRange能拿到NSAttributedString指定range的纯文本。具体实现如下:
@implementation NSAttributedString (PPAddition) - (NSString *)pp_plainTextForRange:(NSRange)range { if (range.location == NSNotFound || range.length == NSNotFound) { return nil; } NSMutableString *result = [[NSMutableString alloc] init]; if (range.length == 0) { return result; } NSString *string = self.string; [self enumerateAttribute:PPTextBackedStringAttributeName inRange:range options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) { PPTextBackedString *backed = value; if (backed && backed.string) { [result appendString:backed.string]; } else { [result appendString:[string substringWithRange:range]]; } }]; return result; } @end @implementation NSMutableAttributedString (PPAddition) - (void)pp_setTextBackedString:(PPTextBackedString *)textBackedString range:(NSRange)range { if (textBackedString && ![NSNull isEqual:textBackedString]) { [self addAttribute:PPTextBackedStringAttributeName value:textBackedString range:range]; } else { [self removeAttribute:PPTextBackedStringAttributeName range:range]; } } @end
灵活的光标
表情功能,UITextView都是用NSAttributedString进行赋值的,并且我们底层其实还是用上面说到的纯文本进行实现的,那么把 [笑死] 转成 ? 就会从4个字符变成1个字符,这里是有差值的,如果不处理的话就会出现上面提到的微博国际版中复制黏贴输入框的表情会导致光标位置不对,甚至莫名其妙多出前后空格的问题。为了精准的定位光标,我们需要自行处理好这些问题。
这里自己继承并实现了UITextView的子类PPStickerTextView,在这个类中重载复制、黏贴、剪切等操作,分别对应的方法如下:
- (void)cut:(id)sender; // 剪切 - (void)copy:(id)sender; // 复制 - (void)paste:(id)sender; // 黏贴
下面以剪切方法举例,看看怎么处理光标的问题,需要注意的地方请看对应的注释:
- (void)cut:(id)sender { // 1.从textView中拿到对应的纯文本,比如:笑死了[笑死] NSString *string = [self.attributedText pp_plainTextForRange:self.selectedRange]; if (string.length) { // 2. 将纯文本写入到剪贴板中 [UIPasteboard generalPasteboard].string = string; // 3. 记住当前的光标位置 NSRange selectedRange = self.selectedRange; NSMutableAttributedString *attributeContent = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; // 4. 将检测到是表情的文本替换成对应的图片 [attributeContent replaceCharactersInRange:self.selectedRange withString:@""]; self.attributedText = attributeContent; // 5. 重新设置光标 self.selectedRange = NSMakeRange(selectedRange.location, 0); } }
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。