UITextView - один из наиболее часто используемых элементов в приложениях iOS. В основном, когда мы хотим позволить пользователям писать более длинные тексты, например. получать от них обратную связь.

Во многих проектах я встречал требование дизайнеров - иметь TextView, который автоматически адаптирует свою высоту к предоставленному контенту. К счастью, это довольно простая задача. Чтобы добиться этого, нам нужно начать с отключения прокрутки для текстового просмотра, просто установив:

 textView.isScrollEnabled = false

Это даст автоматическому макету подсказку, что если он хочет отображать весь контент, ему нужно будет развернуть TextView. Второе требование - добавить некоторые ограничения, сообщающие нашему взгляду, в каком направлении ему разрешено расширяться. Например, мы можем прикрепить его к сторонам Top (), Left () и Right () родительского представления.

Так это выглядело бы как-то так:

self.view.addSubview(textView)
textView.easy.layout(Left(10), Right(10), Top(200))

Примеры кода, которые я представляю в этой статье, выполнены с использованием EasyPeasy, фреймворка, который делает написание автоматической компоновки более прозрачным и более приятным для чтения.

Результатом вышеуказанных изменений являются следующие:

Вариант этой проблемы, который очень часто можно увидеть в StackOverflow, - это требование, которое связывает две функции TextView - его источник прокрутки и скорость реагирования на размер. Другими словами, часто просят расширить TextView до определенного размера после того, как он будет достигнут и пользователь предоставит больше контента - используйте прокрутку, чтобы позволить ему это сделать. Это может быть довольно сложно, поэтому давайте разделим его на основные шаги:

  • Расширьте TextView до определенного размера - мы уже сделали это!
  • По достижении - нам нужен способ измерить, какой размер поля у нас есть,
  • И пользователь предоставляет больше контента - мы не хотим никоим образом блокировать ввод,
  • Используйте прокрутку, чтобы позволить ему сделать это - так что нам нужно снова включить прокрутку.

Перейдем непосредственно к коду.

Как видите, мы добавили переменную isOversized с наблюдателем свойств didSet. Мы также назначили UITextViewDelegate, который позволяет нам действовать каждый раз, когда пользователь изменяет TextView.

Функциональность, помещенная в вызов textViewDidChange, представляет собой вычисление размера, используемого TextView, и проверку того, превышает ли он максимальную высоту, выбранную нами. Если это так - то мы присваиваем True значение is Oversized.

func textViewDidChange(_ textView: UITextView) { 
  if textView.contentSize.height >= maxHeight { 
     isOversized = true 
  } 
}

Обратите внимание, что мы не присваиваем результат условия if напрямую, например:

isOversized = textView.contentSize.height >= maxHeight

в этом конкретном случае мы не хотим вызывать наблюдателя didSet при каждом изменении текста - мы ждем выполнения условия и только затем устанавливаем значение свойства.

Что касается didSet снова - textView.easy.reload () делает именно то, что он говорит - перезагружает ограничения. Он необходим для закрытия .when ({[unowned self] in self.isOversized}) для применения. Подробнее об этом можно прочитать здесь.

Результат кажется довольно приятным:

но у него есть одна вещь, которая все еще может быть лучше - после удаления некоторого содержимого textView остается на максимальной высоте. Чтобы улучшить это, нам нужно сделать несколько небольших настроек.

Сначала изменим textViewDidChange

func textViewDidChange(_ textView: UITextView) {
   isOversized = textView.contentSize.height > maxHeight
}

Точно так же, как я написал выше, что это может быть не лучшая идея;)
Чтобы решить проблему с переопределенным наблюдателем, мы можем добавить охрану, проверяющую, изменилось ли значение:

Мы также добавили вызов setNeedsUpdateConstraints (). Это важно, потому что в противном случае textView может «пропустить» момент, когда ему нужно начать автоматически пересчитывать размер содержимого для отключенной прокрутки, что может привести к сжатию нашего TextView в одну строку до тех пор, пока пользователь не предоставит принудительное изменение текстового представления (добавив новую строку для пример).

Результат такой:

Надеюсь, что эта статья окажется для вас полезной или вы найдете одну недостающую строчку кода, которая мешала полному успеху :)

В следующей части мы попытаемся немного усложнить ситуацию и посмотрим, как управлять ситуациями, когда максимальный размер textView также должен быть динамическим, например, на экранах меньшего размера, когда мы отображаем экранную клавиатуру.