我在键盘上方有一个称为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
的位置,然后仅更改的位置。当您更改高度并开始键入多行时,建议仅在需要时更改高度。