欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS实现UITextView自动增长高度以及自动滚动避免键盘遮挡

程序员文章站 2022-06-01 14:05:44
...

首先我们下面的讨论是建立在Eureka使用的基础上的.

Eureka中包含一个TextAreaRow,其中引用了一个标准的UITextView对象.

一.自动增长UITextView高度

很容易!只要下面一句:

todoRow = TextAreaRow(){row in
    row.textAreaHeight = TextAreaHeight.dynamic(initialTextViewHeight: 30)
}

上面我们设置TextAreaRow的高度为动态,这意味着textView会随着输入自动改变高度!注意这使得row.cell.textView.isScrollEnabled被强制赋值为false!

棒棒哒! :)

二.UITextView自动滚动避免键盘遮挡

这也很容易…额…好吧,没那么容易 ;(

我开始尝试采用根据UITextView的高度,修改其内容偏移来实现:

var offsetY:CGFloat
if row.cell.textView.bounds.height < 400{
    offsetY = 0
}else{
    offsetY = row.cell.textView.bounds.height - 400
}

row.cell.textView.setContentOffset(CGPoint(x: 0, y: offsetY), animated: false)

不过很快发现这样行不通!原因是需要修正的offsetY的值和textView高度竟然不是线性关系… ;(

经过多次测试以及人脑分析,总结出一个初略的逼近关系:

if row.cell.textView.bounds.height < 400{
    offsetY = 0
}else if row.cell.textView.bounds.height < 800{
    offsetY = row.cell.textView.bounds.height - 400
}else if row.cell.textView.bounds.height < 1200{
    offsetY = row.cell.textView.bounds.height - 800
}else if row.cell.textView.bounds.height < 1600{
    offsetY = row.cell.textView.bounds.height - 1200
}

虽说谁也不会吃饱了没事干输入超过1600px高度的内容,不过我还是将其归纳成一个”好看的多”的函数表示:

///粗略估计需要偏移textView Y坐标的值
func figureOutContentOffsetY(textViewHeight:CGFloat)->CGFloat{
    var i:CGFloat = 1
    var offsetY:CGFloat = 0.0

    while textViewHeight >= (i * 200){
        i += 1
    }

    if i == 1{
        offsetY = 0
    }else{
        offsetY = textViewHeight - 180 * (i-1)
    }
    return offsetY
}

但这样怎么说都不太精确,给用户的体验可想而知.人家happy的输入着突然发现你的UI好似抽了风似的痉挛…所以还得换种方法 :)

我发现UITextView有一个实例方法scrollRangeToVisible(),So应该找到需要显示的Range,然后让它可见就好啦:

var sltRange = textView.selectedRange
sltRange.length = 1
sltRange.location -= 1

textView.scrollRangeToVisible(sltRange)

不过抱歉,这样一点毛用都没有,别问我为什么,我也不知道 ;(
(PS:网上查了一下,有人说是需要键盘收起后才可以生效,如果是这样的话还是不符合要求!)

接下来寻思着似乎可以通过textView的底部和键盘上沿的比较来修正textView的位置.为了达到这个目的,我们需要注册键盘的弹出和收起的回调:

var kbRect:CGRect?

@objc func keyboardDidHidden(notification: Notification) {
    kbRect = nil
}

@objc func keyboardDidShow(notification: Notification) {
    let userInfo = notification.userInfo!
    kbRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
}

然后在textView内容发生改变时计算要修正的偏移量:

todoRow = TextAreaRow(){row in
    //初始化row...
}.onChange {row in
    let textView = row.cell.textView!

    if let kbRect = self.kbRect{
        self.view.transform = .identity
        let tfRect = textView.superview!.convert(textView.frame, to: self.view)
        let tfMaxY = tfRect.maxY
        let kbMinY = kbRect.minY

        if kbMinY < tfMaxY && tfMaxY - kbMinY > 20{
            let distanceMoved = tfMaxY - kbMinY
            UIView.animate(withDuration: 0.3){
                textView.isScrollEnabled = false
                textView.transform = CGAffineTransform(translationX: 0, y: -distanceMoved - 20)
                textView.isScrollEnabled = true
            }
        }
}

运行一下,貌似很好…不过当你选择从textView中间位置开始插入内容时开始露馅了…因为textView高度没变,所以textView和键盘的偏移没有得到正确的修正…

经过短暂的思考,本猫意识到应该计算的不是textView下沿和键盘上沿的偏移,而是textView插入符的下沿和键盘上沿的偏移…

所以怎么得到textView里插入符的位置很重要:

var sltRange = textView.selectedTextRange!
let pos = textView.position(within: sltRange, farthestIn: .down)
var caretRect = textView.caretRect(for: pos!)
caretRect = textView.superview!.convert(caretRect, to: self.view)
let caretMaxY = caretRect.maxY

后面就简单了,我们只需将前面代码中的tfMaxY换为caretMaxY就可以了.

最后欣赏下劳动果实:

iOS实现UITextView自动增长高度以及自动滚动避免键盘遮挡

本文是跟随本猫思绪所写,所以有点乱,不过习惯就好…

感谢观赏 ;)

上一篇: Android 面试集锦

下一篇: c++模板