TextContainer UITextView отображает в неправильном кадре при изменении высоты - PullRequest
0 голосов
/ 08 ноября 2018

Все, что я хочу, это изменить размер 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 и некоторые другие хакерские решения, но я просто не могу это исправить. Как я могу решить это?

Дополнительная информация:

Посмотрев на отладчик представлений, вот как это выглядит при вводе введите (перевод строки)

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, потому что оно не охватывает все ошибки.

Примечание:

Поддержка ОС: iOS 8.xx - iOS 12.xx Xcode: v10.10 Свифт: 3 и 4 (пробовал на обоих)

PS: да, я уже отвечал на другие вопросы SO, такие как перечисленные внизу и некоторые другие

1 Ответ

0 голосов
/ 08 ноября 2018

Вы можете игнорировать весь код для вычисления высоты и использовать установленную прокрутку атрибута textview. Enabled = false textview настроит себя так, чтобы соответствовать текстовому содержимому image1

затем измените код, как хотите

@IBOutlet weak var textView: UITextView!

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

Результат будет таким image2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...