美文网首页将来跳槽用码农的世界程序员
用UITextView撸一个顺畅的评论输入框

用UITextView撸一个顺畅的评论输入框

作者: NotFunGuy | 来源:发表于2018-05-10 17:35 被阅读77次

对比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;

sizeToFitsizeThatFits:是有一定区别的,官方文档注释:

// 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是设置内边距,但是滚动条的位置并不会改变,依然是贴着右边的,如果是设置有较大的圆角的回,会影响视觉效果。

image

textContainerInsetcontentInset是不一样的,是在文本视图内容区域内设置inset:

Inset the text container's layout area within the text view's content area

如果我们想要改变光标的位置,可以通过textContainerInset做到,比如现在光标和文字内容中间不在一条线上,应该让光标的中心和文本的中心在一条线上

image
可以通过textContainerInset作微调。
image

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实现一个评论框

image

这是实现之后的效果,实现起来也比较简单,值得注意的是这几个地方:

  1. 输入框高度的动态变化
  2. 弹出的动画
  3. 滚动条的位置
  4. iPhone X的适配
  5. 光标的位置

输入框高度的变化需要在UITextView的代理方法textViewDidChange:里面去实现,主要是计算当前文字的高度,然后调整输入框的整体高度和位置。有两种实现高度动态改变的方式:

  1. 高度自适应
  2. 超过一行的时候变为指定高度

最初可能会觉得高度自适应是一种很好的方式,但是后来发现这样在一行和两行切换的时候会不顺畅。以第二种方式实现比较简便,并且效果也很顺畅,通常会给定两个或者三个高度,在textViewDidChange:进行切换即可。

弹出动画指的是,在点击底部的写评论的时候,需要从下面弹出到键盘上面的动画效果,需要和键盘的弹出一致。

实现思路暂时分为两种:

  1. 整体是一个自定义的View,当点击的时候改变它的frame,同时里面的内容也需要改变。
  2. 分为两个自定义的Veiw,底部的是一个toolView,输入框是另外一个editView。创建之后将editView先隐藏并放在最底部,当点击底部的toolView时,显示editView并改变位置。这样达到的动画效果也是很流畅的。

这里推荐采用第二种方式实现,输入框并不一定在这个地方使用,其他地方也可以用,是一个独立的View。

同时为了很好解决滚动条的位置问题,可以在UITextView底部包装一层UIView,这样就不需要通过设置UITextView的contentInset属性来实现左右间距,滚动条也不会被遮挡。

同时将发送结果以代理的方式暴露出来。

demo地址

动态改变高度的输入框

相关文章

网友评论

    本文标题:用UITextView撸一个顺畅的评论输入框

    本文链接:https://www.haomeiwen.com/subject/hjrtdftx.html