表情键盘基本布局
表情键盘基本的实现逻辑想必大多数人都有所了解,在支持输入的原生控件(UITextView、UITextFieeld)中都会有两个属性inputView、inputAccessoryView,就是替换原生键盘中的inputView来实现自定义的键盘view,用inputAccessoryView来实现键盘顶部的最近使用等,基本都逃不出这个逻辑。
@property (nullable, readwrite, strong) UIView *inputView;
@property (nullable, readwrite, strong) UIView *inputAccessoryView;
如下图左边为抖音的文字键盘,右图为微信的表情键盘,抖音的文字键盘中的绿色框部分即为自定义的inputAccessoryView也就是所谓的最近使用的表情,微信的表情键盘中的红色框部分即为自定义的inputView 如下图所示在表情键盘中,也就是自定义的inputView中通常会有这种可以支持左右滑动切换表情包类型,上下滑动展示同一表情包中的表情的逻辑,其实就是在UIScrollView中包裹三个CollectionView来实现。
// LFTEmotionInputView为自定义的inputView
- (LFTEmotionInputView *)inputView {
if (!_inputView) {
// [EmotionManger getEmoteModel]为获取表情键盘数据
_inputView = [[LFTEmotionInputView alloc] initWithEmoModel:[EmotionManger getEmoteModel] frame:CGRectMake(0, 0, UI_SCREEN_WIDTH, 324)];
@weakify(self)
_inputView.emoteClick = ^(EmoteVO * model) {
@strongify(self)
// 这里可以处理表情点击事件
};
_inputView.deleteClick = ^(UIButton * deleBtn) {
@strongify(self)
// 这里可以处理表情键盘中的删除事件
};
}
return _inputView;
}
******************************************************************************
// LFTEmotionAccessoryView为自定义的inputAccessoryView
- (LFTEmotionAccessoryView *)recentView {
if (!_recentView) {
//[EmotionManger getOftenModel]为获取最近使用的表情数据
_recentView = [[LFTEmotionAccessoryView alloc] initWithFrame:CGRectMake(0, 0, UI_SCREEN_WIDTH, 44) recentModels:[EmotionManger getOftenModel]];
@weakify(self)
_recentView.recentEmoteClick = ^(EmoteVO *model) {
@strongify(self)
// 这里可以处理最近使用中的表情点击事件
};
}
return _recentView
}
表情键盘与文字键盘的切换
目前看到的app中的表情键盘与文字键盘的切换分为两种,一种是微信抖音那样的点击切换按钮后,有一个表情键盘(文字键盘)向下渐隐到文字键盘(表情键盘)向上渐显的动画转场效果,这种效果也就是先让输入框resignFirstResponder,但是输入框的位置不会下落,然后再为其重新设置inputView和inputAccessoryView,最后becomeFirstResponder,就可以达到类似的这种效果,当然也有那种输入框下面就是表情键盘view的布局逻辑这种和替换键盘的inputView就有本质的区别了。
// 点击切换按钮调用的方法(省略大部分的数据赋值逻辑和监听键盘弹出和隐藏对输入框位置的处理逻辑,仅保留了简单的切换逻辑)
- (void)keyBoardChange {
if (self.emotionChangeBtn.tag == 0) { // 通过记tag判断tag == 0切换到表情键盘
self.emotionChangeBtn.tag = 1;
[self.textView resignFirstResponder];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.textView.inputView = self.inputView; // 自定义的表情view
self.textView.inputAccessoryView = nil;
[self.textView becomeFirstResponder];
});
} else { // 切换到文字键盘
self.emotionChangeBtn.tag = 0;
self.emotionChangeBtn.svg_ImageName = @"toolbar_icon_emoji.svg";
[self.textView resignFirstResponder];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.textView.inputView = nil;
self.textView.inputAccessoryView = self.recentView; // 自定义的最近使用view
}
[self.textView becomeFirstResponder];
self.isEmoteBtnClick = NO;
});
}
}
另一种就是类似微博的那种的切换效果,没有相关的专场动画过渡,大概逻辑如下:
- (void)keyBoardChange {
self.isEmoteBtnClick = YES;
if (self.emotionChangeBtn.tag == 0) { //表情键盘
self.emotionChangeBtn.tag = 1; // emotionChangeBtn为输入框右边的切换图标按钮
self.emotionChangeBtn.svg_ImageName = @"toolbar_icon_keyboard.svg";
self.textView.inputView = self.inputView;
self.textView.inputAccessoryView = nil;
[self.textView reloadInputViews];
} else { // 普通键盘
self.emotionChangeBtn.tag = 0;
self.emotionChangeBtn.svg_ImageName = @"toolbar_icon_emoji.svg";
self.textView.inputView = nil;
self.textView.inputAccessoryView = self.recentView;
[self.textView reloadInputViews];
}
[self.textView becomeFirstResponder];
}
特殊注意点
但是微博的表情键盘有一个功能就是在点击输入框并且不失去焦点的情况下(输入框中一直显示光标)可以切换inputView,使其从表情键盘切换到普通的文字键盘,这是怎么做到的呢?知乎上也有人提出了同样的疑问www.zhihu.com/question/51… 其实仔细研究下我们不难发现,我们可以给TextView添加一个点击手势,但是textView本来就可以响应点击事件,所以要在点击代理中做一下处理,然后在点击手势中做一些文章就可以达到微博的这种效果了,主要的逻辑如下:
// 添加点击手势
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(TextViewClick)];
tapGesture.delegate = self;
[self.textView addGestureRecognizer:tapGesture];
// 支持多手势,让textView的点击事件可以被识别,默认为no,如果这里不处理就不会出发TextViewClick方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return YES;
}
return NO;
}
//输入框点击切换
- (void)TextViewClick {
if (self.emotionChangeBtn.tag == 1) {
[self keyBoardChange];
} else {
[self.textView becomeFirstResponder];
}
}