更改高度后,UITextView的textContainer呈现在错误的帧上

时间:2018-11-08 11:05:23

标签: ios swift uitextview

我想做的就是只要在UITextView上输入/粘贴大小即可。它应该是可滚动的,因为我不希望UITextView填满屏幕。这几行代码很容易实现。

@IBOutlet weak var mainTextView: UITextView!
@IBOutlet weak var mainTextViewHeightConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    self.mainTextView.delegate = self
}

// This method is used for calculating the string's height in a UITextField
func heightOf(text: String, for textView: UITextView) -> CGFloat {
    let nsstring: NSString = text as NSString
    let boundingSize = nsstring.boundingRect(
        with: CGSize(width: textView.frame.size.width, height: .greatestFiniteMagnitude),
        options: .usesLineFragmentOrigin,
        attributes: [.font: textView.font!],
        context: nil).size
    return ceil(boundingSize.height + textView.textContainerInset.top + textView.textContainerInset.bottom)
}

// This method calculates the UITextView's new height
func calculateHeightFor(text: String, in textView: UITextView) -> CGFloat {
    let newString: String = textView.text ?? ""
    let minHeight = self.heightOf(text: "", for: textView) // 1 line height
    let maxHeight = self.heightOf(text: "\n\n\n\n\n", for: textView) // 6 lines height
    let contentHeight = self.heightOf(text: newString, for: textView) // height of the new string
    return min(max(minHeight, contentHeight), maxHeight)
}

// The lines inside will change the UITextView's height
func textViewDidChange(_ textView: UITextView) {
    self.mainTextViewHeightConstraint.constant = calculateHeightFor(
        text: textView.text ?? "", 
        in: textView)
}

除了粘贴多行文本或按输入(换行)时,此方法效果很好。


为解决粘贴问题,各种SO问题建议我要么创建UITextView的子类以覆盖paste(_:)方法,要么使用textView(_: shouldChangeTextIn....)委托方法。经过两个最佳答案的实验之后,使用了shouldChangeTextIn方法,这使我的代码成为了

// I replaced `textViewDidChange(_ textView: UITextView)` with this
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    self.mainTextViewHeightConstraint.constant = calculateHeightFor(
        text: ((textView.text ?? "") as NSString).replacingCharacters(in: range, with: text), 
        in: textView)
    return true
}

现在应该发生的是,当您粘贴文本时,UITextView应该看起来像这样。 (粘贴的字符串为“ A \ nA \ nA \ nA \ nA”或5行,仅包含A)

enter image description here

但是变成这样

enter image description here

从屏幕截图中可以看到,textContainer的框架位于错误的位置。现在,关于此的奇怪的事情是,它似乎仅在您粘贴到空的UITextView时才会发生。

我尝试设置和重置UITextView的frame.size,我尝试过手动设置NSTextContainer的大小,以及其他一些hacky解决方案,但我似乎无法要解决这个问题。我该怎么解决?

其他信息:

在查看视图调试器时,这是您键入 enter (换行符)

时的外观

enter image description here enter image description here

如我们所见,文本容器在错误的位置。


毕竟,添加一个UIView.animate块似乎可以解决部分问题,但不是全部。为什么这样做?

// These snippet kind of fixes the problem
self.mainTextViewHeightConstraint.constant = calculateHeightFor(
    text: textView.text ?? "", 
    in: textView)
UIView.animate(withDuration: 0.1) {
    self.view.layoutIfNeeded()
}

注意:我没有放置UIView.animate解决方案,因为它没有涵盖所有错误。

注意:

OS支持:iOS 8.xx-iOS 12.xx Xcode:v10.10 斯威夫特:3和4(我都尝试过)

PS:是的,我已经在其他SO问题中,例如底部和其他一些问题

1 个答案:

答案 0 :(得分:0)

您可以忽略所有用于计算高度的代码,并使用set textview属性滚动Enabled = false textview将自行调整以适合文本内容 image1

然后根据需要更改代码

@IBOutlet weak var textView: UITextView!

override func viewDidLoad() {
    super.viewDidLoad()
    self.textView.text = "A\nA\nA\nA\nA"
}

结果将是这样 image2