我在键盘上方有一个称为composeTextView的UITextView。此UITextView在名为composeUIView的视图内。用户输入文本时,TextView会调整大小以显示所有文本。我的问题是,当我尝试调整composeUIView的底部约束时,composeTextView的大小将停止更改以适合其内容。我不明白这里发生了什么或如何解决。如果有人知道解决方法,我可以使用帮助。

长话短说,随着用户增加使用的行数,我试图显示更多的UITextView行,然后移动UITextView向上的UIView,以使显示的多余行不被键盘覆盖。

class ChatViewController: UIViewController, UITextViewDelegate {

@IBOutlet weak var composeTextView: UITextView!
@IBOutlet weak var composeViewBottomConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()

    composeTextView.delegate = self
}

func textViewDidChange(_ textView: UITextView) {

    let oldHeight = composeTextView.frame.height

    let fixedWidth = composeTextView.frame.size.width
    composeTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
    let newSize = composeTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
    var newFrame = composeTextView.frame
    newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
    composeTextView.frame = newFrame

    let newHeight = composeTextView.frame.height

    let changeHeight = newHeight - oldHeight

    if changeHeight != 0 {
        self.composeViewBottomConstraint.constant += changeHeight
    }
}

编辑1:
根据Michael的建议,我进行了此更改,但并不一致。如果我按下退格按钮,并且它一次开始删除单词,那么最后它的结尾将小于正常的TextView。另外,如果我粘贴大量文本或删除大量文本,则不能正确响应:
var oldNumLines = CGFloat()

override func viewDidLoad() {
    super.viewDidLoad()

    composeTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

    self.oldNumLines = composeTextView.contentSize.height / (composeTextView.font?.lineHeight)!

}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    let newNumLines = composeTextView.contentSize.height / (composeTextView.font?.lineHeight)!

    print("oldNumLines: \(oldNumLines)")
    print("newNumLines: \(newNumLines)")

    let diffNumLines = newNumLines - oldNumLines
    print("diffNumLines: \(diffNumLines)")

    let heightChange = (diffNumLines * (composeTextView.font?.lineHeight)!)
    print("heightChange: \(heightChange)")

    if diffNumLines > 1 {
        if composeTextView.frame.size.height < (5 * (composeTextView.font?.lineHeight)!) {
    composeTextView.frame.size.height += heightChange
    composeViewHeightConstraint.constant += heightChange
    }
    }

    if diffNumLines < -1 {

// the number 1.975216273089 is the first value of newNumLines
        if newNumLines > 1.975216273089 && composeTextView.frame.size.height < (5 * (composeTextView.font?.lineHeight)!) {
            composeTextView.frame.size.height += heightChange
            composeViewHeightConstraint.constant += heightChange
        }
    }

    if composeTextView.frame.size.height >= (5 * (composeTextView.font?.lineHeight)!) && composeTextView.frame.size.height < (6 * (composeTextView.font?.lineHeight)!) && diffNumLines < -1 {

        composeTextView.frame.size.height += heightChange
        composeViewHeightConstraint.constant += heightChange
    }

    self.oldNumLines = newNumLines
}

编辑2:
我想出了解决最终问题的方法。我正在检查旧行数和新行数(diffNumLines)的差是否大于1或小于-1。我没有意识到有时diffNumLines约为.95或-.95。在某些情况下,diffNumLines大约为.44,而我不想包括在内,因此我将所有比较diffNumLines值的值更改为1或-1,因此将其与.75或-.75进行了比较。然后,我添加了几行,在其中检查heightChange是否大于5行的高度或小于5行的负高度。通过使用数字5实际找不到5行的高度,我必须获得5行的实际高度。我在调试控制台和一些打印语句中发现,如果一次输入一个单词,composeTextView的高度将停止在100处增加。它的起始高度为33,因此简单的减法告诉我们,我最想更改的地方是height是67。我设置了两个规则,因此,如果heightChange> 67,heightChange将被设置回67,如果heightChange
我只有最后一个担心,它确实不需要解决,但可以证明是有用的。如果我尝试动画化高度的变化,它会显示composeTextView弹出到新的y位置,然后对底部进行动画处理以向下填充其高度。这看起来真的很丑,所以我决定不使用动画。我希望的是composeView的底部将保持在原处,并且其顶部将进行动画处理以填充其高度。我认为这将需要更改其顶部约束而不是其高度,我宁愿不要再做一遍。

我对它感到满意,但仍有改进的空间。

这是我的最终代码:
@IBOutlet weak var composeTextItem: UIBarButtonItem!
@IBOutlet weak var composeUIView: UIView!
var oldNumLines = CGFloat()

override func viewDidLoad() {
    super.viewDidLoad()

    composeTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
    // the number 1.975216273089 is the first value of newNumLines
    self.oldNumLines = 1.97521627308861

}

deinit {
    composeTextView.removeObserver(self, forKeyPath: "contentSize")
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    let newNumLines = composeTextView.contentSize.height / (composeTextView.font?.lineHeight)!

    let diffNumLines = newNumLines - oldNumLines

    var heightChange = (diffNumLines * (composeTextView.font?.lineHeight)!)

    if heightChange > 67 {
        heightChange = 67
        }
    if heightChange < -67 {
        heightChange = -67
    }

    if diffNumLines > 0.75 {
        if composeTextView.frame.size.height < (5 * (composeTextView.font?.lineHeight)!) {
    composeTextView.frame.size.height += heightChange
    composeViewHeightConstraint.constant += heightChange
    }
    }

    if composeTextView.frame.size.height > 33 {
    if diffNumLines < -0.75 {
        // the number 1.975216273089 is the first value of newNumLines
        if newNumLines > 1.97521627308861 && composeTextView.frame.size.height < (5 * (composeTextView.font?.lineHeight)!) {
            composeTextView.frame.size.height += heightChange
            composeViewHeightConstraint.constant += heightChange
        }
    }

    if composeTextView.frame.size.height >= (5 * (composeTextView.font?.lineHeight)!) && composeTextView.frame.size.height < (6 * (composeTextView.font?.lineHeight)!) && diffNumLines < -0.75 {

        composeTextView.frame.size.height += heightChange
        composeViewHeightConstraint.constant += heightChange
    }
    }

    self.oldNumLines = newNumLines
}

最佳答案

我不确定为什么要尝试更改包含textview的视图的底部约束...如果要更改composeTextView的高度,那么还应该将composeView的高度更改相同的数量。

另外,我不使用textViewDidChange,而是将contentSize的观察者添加到文本视图中,然后只要内容大小更改,该观察器便会调用一个函数。它使您的生活更加轻松-如果您使用的是Swift 4,则如下所示:

composeTextView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

请记住在composeTextView.removeObserver(self, forKeyPath: "contentSize")中调用deinit

然后,覆盖observeValue函数:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    // Change height of composeTextView and composeView here
}

适应高度变化的一种好方法是找出行数与行数之差,因为您知道每次调用此函数时,行数都是不同的。要获得行数,您可以执行以下操作(快速4):
let numLines = composeTextView.contentSize.height / (composeTextView.font?.lineHeight)!

计算行的差,将其乘以composeTextView.font?.lineHeight,然后将其作为更改所有内容的高度。

希望这可以帮助。

编辑

如果您正确设置约束,则不需要上移composeView,这将比需要的工作更多。当composeView的高度更改时,底部不应该移动-仅顶部应该移动。 当您显示键盘时,建议您然后更改composeView的位置,然后仅更改的位置。当您更改高度并开始键入多行时,建议仅在需要时更改高度。

07-26 09:38