对比UITextField
- 继承关系:
UITextField继承自UIControl,UITextView继承自UIScrollView; - 输入行数
UITextView支持多行输入,可以滑屏垂直滚动,UITextField仅支持单行输入 - Placeholder
UITextField支持设置Placeholder属性,而UITextView则没有这个功能。
UITextView重要属性
1. 键盘的重要通知
NSNotificationName const UIKeyboardWillShowNotification
NSNotificationName const UIKeyboardDidShowNotification
NSNotificationName const UIKeyboardWillHideNotification
NSNotificationName const UIKeyboardDidHideNotification
NSNotificationName const UIKeyboardWillChangeFrameNotification
NSNotificationName const UIKeyboardDidChangeFrameNotification
从上到下分别是键盘的显示、隐藏、Frame改变。
值得注意的是,在弹出和隐藏键盘的时候,这几个通知都只会调一次,而通常使用UIKeyboardWillChangeFrameNotification通知就能实现大部分的情况,Frame改变就包含了Show和Hide。
如果只用前面两个通知的话,键盘弹出之后切换为其他类型的键盘的情况是监听不到的,不同的键盘高度是不同的。
获取键盘的高度:
// 在合适的地方注册通知
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
// 注意需要移除通知
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// 监听到键盘Frame改变
- (void)keyboardWillChangeFrame:(NSNotification *)note {
CGFloat keyboardHeight = [[[note userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
}
2. UITextView的scrollEnabled属性
多行文字使用UITextView能便捷实现换行,但是在文字刚换行的时候可能会出现文字整体上移然后回落的效果,这个或许是由于UITextView继承自UIScrollView的缘故,禁用scrollEnabled就不会出现这样的效果。
但是如果是使用UITextView提供一个小区域的输入框,当多行文字的时候scrollEnabled是必须要开启的,这个时候可以通过在UITextView的代理方法textViewDidChange:
里面去判断是否是多行文字,并作出相应的处理:
- (void)textViewDidChange:(UITextView *)textView{
// 计算文字的高度
CGSize size = [textView sizeThatFits:CGSizeMake(textView.width, MAXFLOAT)];
//oneLineHeight是提前计算好的一行文字的高度
if (size.height <= self.oneLineHeight) {
// 单行,禁用滚动
textView.scrollEnabled = NO;
} else {
// 多行,先实现高度变化
// change textView height code
// 然后再开启滚动
textView.scrollEnabled = YES;
}
...
计算字体的高度:
CGSize size = [textView sizeThatFits:CGSizeMake(textView.width, MAXFLOAT)];
CGFloat height = size.hight;
sizeToFit
和sizeThatFits:
是有一定区别的,官方文档注释:
// return 'best' size to fit given size. does not actually resize view. Default is return existing view size
- (CGSize)sizeThatFits:(CGSize)size;
// calls sizeThatFits: with current view bounds and changes bounds size.
- (void)sizeToFit;
sizeToFit
的内部实现是调用sizeThatFits:
实现的。
sizeToFit
:会计算出最优的size,会改变自己的size。
sizeThatFits:
: 会计算出最优的size,但是不会改变自己的size。
3. textContainerInset和contentInset的区别?
contentInset
是设置内边距,但是滚动条的位置并不会改变,依然是贴着右边的,如果是设置有较大的圆角的回,会影响视觉效果。
![](https://img.haomeiwen.com/i3738156/a9e437147fc87602.png)
textContainerInset
和contentInset
是不一样的,是在文本视图内容区域内设置inset:
Inset the text container's layout area within the text view's content area
如果我们想要改变光标的位置,可以通过textContainerInset
做到,比如现在光标和文字内容中间不在一条线上,应该让光标的中心和文本的中心在一条线上
![](https://img.haomeiwen.com/i3738156/c8051ae87a59c33c.png)
可以通过textContainerInset作微调。
![](https://img.haomeiwen.com/i3738156/5bad10991ed28b45.png)
4. UITextView没有placeholder? !
UITextView的最大缺陷应该是没有像UITextField那样的Placeholder属性。
但是实现起来也不麻烦,这里提供几种思路:
1. 将UITextView的文本属性当成临时的占位符
- 把UITextView的文本属性当成“临时的占位符”使用。
- 在开始编辑的代理方法里清除“临时的占位符”
- 在结束编辑的代理方法里根据条件设置“临时的占位符”。
特点:
- 当用户点击了TextView的时候,占位符占位文字就会立马消失,官方的占位符是当系统监听到用户输入了文字后占位符才会消失。
2. 给UITextView添加UILabel子控件,作为placeholder
- 给textView添加一个UILabel子控件,作为placeholder
- 在文本改变的代理方法里面显示/隐藏UILabel
特点:
相比于方法一,该方法可以实现动态监听文本的改变,并非弹出键盘就立即清除占位符,只有当用户开始输入文本的时候.placeholder才会消失。同样,当用户清空文本的时候,占位符又会重新显示出来。
3. 自定义UITextView
- 自定义
UITextView
- 给
UITextView
添加占位符属性 - 重写
initWithFrame
方法 - 添加通知监听文字改变
- 重写
drawRect:
方法实现绘制
特点:
相对于上面两种方法,该方法可移植性要好些,拓展性也更强。推荐使用自定义UITextView的方式实现placeholder。
这里是用来通知监听文字改变,其实也可以不用通知,可以在textViewDidChanged:
这个文本改变的代理方法里面监听文字改变。
4. 利用runtime
- UITextView的内部有一个名为“_placeHolderLabel”的私有成员变量,可以在runtime通过KVC来访问私有变量
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 100, 300, 50];
// _placeholderLabel
UILabel *placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.text = @"请输入内容";
placeHolderLabel.textColor = [UIColor lightGrayColor];
[placeHolderLabel sizeToFit];
[textView addSubview:placeHolderLabel];
// KVC
[textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
UITextView实现一个评论框
![](https://img.haomeiwen.com/i3738156/a282dfe5fad22127.png)
这是实现之后的效果,实现起来也比较简单,值得注意的是这几个地方:
- 输入框高度的动态变化
- 弹出的动画
- 滚动条的位置
- iPhone X的适配
- 光标的位置
输入框高度的变化需要在UITextView的代理方法textViewDidChange:
里面去实现,主要是计算当前文字的高度,然后调整输入框的整体高度和位置。有两种实现高度动态改变的方式:
- 高度自适应
- 超过一行的时候变为指定高度
最初可能会觉得高度自适应是一种很好的方式,但是后来发现这样在一行和两行切换的时候会不顺畅。以第二种方式实现比较简便,并且效果也很顺畅,通常会给定两个或者三个高度,在textViewDidChange:
进行切换即可。
弹出动画指的是,在点击底部的写评论的时候,需要从下面弹出到键盘上面的动画效果,需要和键盘的弹出一致。
实现思路暂时分为两种:
- 整体是一个自定义的View,当点击的时候改变它的frame,同时里面的内容也需要改变。
- 分为两个自定义的Veiw,底部的是一个toolView,输入框是另外一个editView。创建之后将editView先隐藏并放在最底部,当点击底部的toolView时,显示editView并改变位置。这样达到的动画效果也是很流畅的。
这里推荐采用第二种方式实现,输入框并不一定在这个地方使用,其他地方也可以用,是一个独立的View。
同时为了很好解决滚动条的位置问题,可以在UITextView底部包装一层UIView,这样就不需要通过设置UITextView的contentInset属性来实现左右间距,滚动条也不会被遮挡。
同时将发送结果以代理的方式暴露出来。
网友评论