Все, что я хочу, это изменить размер 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)
Однако это становится таким
Как видно из скриншотов, рамка textContainer находится в неправильном положении. Теперь странная вещь об этом состоит в том, что это, кажется, происходит только тогда, когда вы вставляете в пустой UITextView
.
Я попытался установить и сбросить UITextView
frame.size, я попытался вручную установить размер NSTextContainer
и некоторые другие хакерские решения, но я просто не могу это исправить. Как я могу решить это?
Дополнительная информация:
Посмотрев на отладчик представлений, вот как это выглядит при вводе введите (перевод строки)
Как мы видим, текстовый контейнер находится в неправильном месте.
После всего этого добавление блока 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, такие как перечисленные внизу и некоторые другие