Как добавить текст-плейсхолдер в UITextView на Swift?
Лучший способ: использовать делегат UITextView и UILabel
Проблема в вашем коде — вы не подписываетесь на изменения текста в UITextView. Вот правильное и надёжное решение, которое используют в реальных проектах.
Шаг 1: Создайте плейсхолдер-лейбл
Добавьте UILabel как дочерний элемент UITextView. Важно: лейбл должен быть неинтерактивным (isUserInteractionEnabled = false), чтобы он не перехватывал касания.
Шаг 2: Реализуйте делегат UITextViewDelegate
Подпишитесь на изменения текста через метод textViewDidChange. В нём проверяйте, пустой ли текст, и скрывайте/показывайте плейсхолдер.
Готовый код
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet weak var textView: UITextView!
let placeholderLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Настройка textView
textView.delegate = self
textView.font = UIFont.systemFont(ofSize: 16)
textView.textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 8, right: 4)
// Настройка плейсхолдера
placeholderLabel.text = "Введите ваш текст..."
placeholderLabel.font = textView.font
placeholderLabel.textColor = UIColor.lightGray
placeholderLabel.numberOfLines = 0 // поддержка многострочного текста
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
placeholderLabel.isUserInteractionEnabled = false // важно!
textView.addSubview(placeholderLabel)
// Auto Layout: привязываем лейбл к textView с учётом отступов
NSLayoutConstraint.activate([
placeholderLabel.topAnchor.constraint(equalTo: textView.topAnchor, constant: textView.textContainerInset.top + 2),
placeholderLabel.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: textView.textContainerInset.left + 4),
placeholderLabel.trailingAnchor.constraint(equalTo: textView.trailingAnchor, constant: -textView.textContainerInset.right - 4)
])
// Начальное состояние: показываем плейсхолдер
placeholderLabel.isHidden = !textView.text.isEmpty
}
// MARK: - UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
// Показываем плейсхолдер, если текст пустой
placeholderLabel.isHidden = !textView.text.isEmpty
}
}
Почему это работает
- Делегат — метод
textViewDidChangeвызывается при каждом изменении текста (ввод, удаление, вставка). - Auto Layout — лейбл автоматически подстраивается под размеры textView, даже если меняются отступы или шрифт.
- isUserInteractionEnabled = false — пользователь может тапать по плейсхолдеру, и касание уйдёт в UITextView, открывая клавиатуру.
- numberOfLines = 0 — если плейсхолдер длинный, он будет переноситься на несколько строк.
Дополнительные улучшения
Если вы используете Storyboard, можно создать подкласс UITextView и добавить плейсхолдер как свойство @IBInspectable. Это сделает компонент переиспользуемым:
class PlaceholderTextView: UITextView {
private let placeholderLabel = UILabel()
@IBInspectable var placeholderText: String = "Введите текст..." {
didSet { placeholderLabel.text = placeholderText }
}
@IBInspectable var placeholderColor: UIColor = .lightGray {
didSet { placeholderLabel.textColor = placeholderColor }
}
override func awakeFromNib() {
super.awakeFromNib()
setupPlaceholder()
}
private func setupPlaceholder() {
// ... код инициализации лейбла (как выше)
NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: UITextView.textDidChangeNotification, object: nil)
}
@objc private func textChanged() {
placeholderLabel.isHidden = !text.isEmpty
}
}
Такой подход удобен: вы просто перетаскиваете PlaceholderTextView в Storyboard и задаёте текст плейсхолдера в инспекторе атрибутов.
Альтернативный ответ: Использование атрибутированного текста
Другой популярный способ — не создавать отдельный UILabel, а использовать атрибутированный текст самого UITextView. Этот метод проще в реализации, но требует аккуратной обработки.
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet weak var textView: UITextView!
let placeholder = "Введите ваш текст..."
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
showPlaceholder()
}
private func showPlaceholder() {
textView.text = placeholder
textView.textColor = UIColor.lightGray
}
private func hidePlaceholder() {
textView.text = ""
textView.textColor = UIColor.black // или ваш основной цвет
}
// MARK: - UITextViewDelegate
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == UIColor.lightGray {
hidePlaceholder()
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
showPlaceholder()
}
}
}
Плюсы и минусы
- + Очень простой код, не нужно создавать дополнительные вью.
- + Работает с любыми шрифтами и отступами "из коробки".
- - Текст плейсхолдера будет отправлен с формой, если вы не проверите цвет. Обязательно фильтруйте ввод:
if textView.textColor != .lightGray { /* отправляем */ }. - - Пользователь может изменить цвет текста или удалить плейсхолдер частично. Это ломает логику.
- - Не подходит, если нужно поддерживать стилизованный текст (NSAttributedString) — они перезапишут плейсхолдер.
Этот метод хорош для простых форм, где плейсхолдер — короткая фраза. Для продакшена я рекомендую первый способ с UILabel — он надёжнее и не влияет на содержимое textView.
Ответов пока нет.
Войдите чтобы оставить ответ