Как добавить текст-заполнитель в UITextView в Swift?


316

Я делаю приложение, которое использует UITextView. Теперь я хочу, чтобы в текстовом представлении был заполнитель, похожий на тот, который вы можете установить для текстового поля. Как бы вы достигли этого с помощью Swift?


Это давняя проблема в разработке iOS с UITextView. Я написал подклассы, подобные упомянутому здесь: stackoverflow.com/a/1704469/1403046 . Преимущество состоит в том, что вы все еще можете иметь делегата, а также использовать класс в нескольких местах без необходимости повторной реализации логики.
cjwirth

Как бы я использовал ваш подкласс, используя swift для проекта. Используя файл моста?
StevenR

Вы можете сделать это, или повторно реализовать это в Swift. Код в ответе длиннее, чем должен быть на самом деле. Главное, чтобы показать / скрыть метку, которую вы добавляете в метод, о котором вы получаете уведомление при изменении текста.
cjwirth

Вы можете использовать пример UIFloatLabelTextView из GitHub. Эта позиция заполнителя сверху при написании. Действительно интересный! github.com/ArtSabintsev/UIFloatLabelTextView
Джайпракаш Дубей

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

Ответы:


649

Обновлено для Swift 4

UITextViewпо сути не имеет свойства заполнителя, поэтому вам придется создавать и манипулировать им программно с помощью UITextViewDelegateметодов. Я рекомендую использовать решение № 1 или № 2 ниже в зависимости от желаемого поведения.

Примечание. Для любого решения добавьте UITextViewDelegateв класс и установите textView.delegate = selfметоды делегата текстового представления.


Решение № 1 - Если вы хотите, чтобы заполнитель исчез, как только пользователь выберет текстовое представление:

Сначала установите в UITextViewкачестве текста-заполнителя и установите его в светло-серый цвет, чтобы имитировать внешний вид UITextFieldтекста-заполнителя. Либо сделайте это в viewDidLoadили при создании текстового представления.

textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

Затем, когда пользователь начинает редактировать текстовое представление, если текстовое представление содержит заполнитель (то есть, если его цвет текста является светло-серым), очистите текст заполнителя и установите цвет текста на черный, чтобы приспособить запись пользователя.

func textViewDidBeginEditing(_ textView: UITextView) {
    if textView.textColor == UIColor.lightGray {
        textView.text = nil
        textView.textColor = UIColor.black
    }
}

Затем, когда пользователь заканчивает редактирование текстового представления и он уходит в отставку как первый респондент, если текстовое представление пусто, сбросьте его заполнитель, повторно добавив текст заполнителя и установив его цвет на светло-серый.

func textViewDidEndEditing(_ textView: UITextView) {
    if textView.text.isEmpty {
        textView.text = "Placeholder"
        textView.textColor = UIColor.lightGray
    }
}

Решение № 2 - Если вы хотите, чтобы заполнитель отображался всякий раз, когда текстовое представление пусто, даже если текстовое представление выбрано:

Сначала установите заполнитель в viewDidLoad:

textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

textView.becomeFirstResponder()

textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)

(Примечание. Поскольку OP хотел, чтобы текстовое представление выбиралось сразу после загрузки представления, я включил выделение текстового представления в вышеприведенный код. Если это не ваше желаемое поведение и вы не хотите, чтобы текстовое представление выбиралось при загрузке представления, удалите две последние строки из указанного выше фрагмента кода.)

Затем используйте shouldChangeTextInRange UITextViewDelegateметод, например так:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    // Combine the textView text and the replacement text to
    // create the updated text string
    let currentText:String = textView.text
    let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)

    // If updated text view will be empty, add the placeholder
    // and set the cursor to the beginning of the text view
    if updatedText.isEmpty {

        textView.text = "Placeholder"
        textView.textColor = UIColor.lightGray

        textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
    }

    // Else if the text view's placeholder is showing and the
    // length of the replacement string is greater than 0, set 
    // the text color to black then set its text to the
    // replacement string
     else if textView.textColor == UIColor.lightGray && !text.isEmpty {
        textView.textColor = UIColor.black
        textView.text = text
    }

    // For every other case, the text should change with the usual
    // behavior...
    else {
        return true
    }

    // ...otherwise return false since the updates have already
    // been made
    return false
}

А также реализовать, textViewDidChangeSelectionчтобы пользователь не мог изменить положение курсора, пока заполнитель не виден. (Примечание: textViewDidChangeSelectionвызывается до загрузки вида, поэтому проверяйте цвет текста только в том случае, если окно видно):

func textViewDidChangeSelection(_ textView: UITextView) {
    if self.view.window != nil {
        if textView.textColor == UIColor.lightGray {
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
    }
}

5
Привет, убедитесь, что вы установили свой контроллер представления в качестве делегата для вашего textView . Вы можете достичь этого, создав выход из вашего textView в ваш viewController. Тогда используйте yourTextField.delegate = self. Если вы не сделаете этого, textViewDidBeginEditingи textViewDidEndEditingфункции не будут работать.
Уджвал-Надхани

2
Код не компилируется, у меня ошибка как Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter

4
Я нашел решение и приложу исправленный код @iPeter. В настоящее время текст должен быть в NSString форманты: let currentText = textView.text as NSString?. Преобразуйте let updatedText =строку в let updatedText = currentText?.replacingCharacters(in: range, with: text). Наконец, преобразуйте if updatedText.isEmptyстроку в if (updatedText?.isEmpty)! {. Это должно делать свое дело!
Бэйлор Митчелл

1
@ TàTruhoada Я обновил решение для обработки сценариев копирования и вставки
Линдси Скотт,

3
Установка @LyndseyScott textView.selectedTextRangeизнутри func textViewDidChangeSelection(_ textView: UITextView)вызывает бесконечный цикл ...
MikeG

229

Плавающий Заполнитель


Это просто, безопасно и надежно расположить метку-заполнитель над текстовым представлением, установить его шрифт, цвет и управлять видимостью-заполнителем, отслеживая изменения количества символов в текстовом представлении.

Свифт 3:

class NotesViewController : UIViewController, UITextViewDelegate {

    @IBOutlet var textView : UITextView!
    var placeholderLabel : UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.delegate = self
        placeholderLabel = UILabel()
        placeholderLabel.text = "Enter some text..."
        placeholderLabel.font = UIFont.italicSystemFont(ofSize: (textView.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        textView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (textView.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !textView.text.isEmpty
    }

    func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
}

Swift 2: То же самое, за исключением: italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor



19
Этот метод, несомненно, является самым простым и наименее подверженным ошибкам, который я нашел. Fabulous.
Дэвид

6
Это хороший метод, но текст метки может выйти за рамки, если текст заполнителя очень длинный ..
Акс

5
Простой в использовании и прекрасно интегрируется с существующим поведением. Сообщение-заполнитель в любом случае не должно быть настолько многословным, однако не позаботится ли об установке этой строки в 0?
Томми С.

2
Я обычно предпочитаю этот метод, хотя проблематично, если текст выровнен по центру, потому что курсор будет находиться по центру над заполнителем, а не слева от него.
blwinters

1
@blwinters - Это был бы действительно необычный случай, не так ли? Но если предположить, что в этом случае это многострочное поле ввода (я должен это предположить), не могли бы вы просто отрегулировать вычисление вертикального смещения, чтобы немного отодвинуть текст-заполнитель от курсора?
ясный свет

35

Настоятельно рекомендуем использовать библиотеку KMPlaceholderTextView . Очень прост в использовании.


1
Это, безусловно, самое простое решение (не включает добавление UILabels или выполнение специальных функций делегатов). Работает из коробки.
HixField

Он должен работать. Автор сказал, что поддерживает Swift
3.0+

27

Swift:

Добавьте текстовое представление программно или через Интерфейсный Разработчик, если последнее, создайте выход:

@IBOutlet weak var yourTextView: UITextView!

Пожалуйста, добавьте делегата (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

В методе viewDidLoad добавьте следующее:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    yourTextView.delegate = self
    yourTextView.text = "Placeholder text goes right here..."
    yourTextView.textColor = UIColor.lightGray

Теперь позвольте мне представить магическую часть, добавив эту функцию:

func textViewDidBeginEditing(_ textView: UITextView) {

    if yourTextView.textColor == UIColor.lightGray {
        yourTextView.text = ""
        yourTextView.textColor = UIColor.black
    }
}

Обратите внимание, что это будет выполняться всякий раз, когда начинается редактирование, там мы проверим условия, чтобы сообщить состояние, используя свойство color. Задавать текст nilя не рекомендую. Сразу после этого мы устанавливаем желаемый цвет текста, в данном случае черный.

Теперь добавьте эту функцию тоже:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

        yourTextView.text = "Placeholder text ..."
        yourTextView.textColor = UIColor.lightGray
    }
}

Позвольте мне настаивать, не сравнить с nil, я уже попробовал это, и это не будет работать. Затем мы устанавливаем значения обратно в стиль заполнителя и устанавливаем цвет обратно в цвет заполнителя, потому что это условие для регистрации textViewDidBeginEditing.


16

Используйте это расширение, это лучший способ установить заполнитель в UITextView. Но убедитесь, что вы прикрепили делегатов к TextView. Вы можете установить Placeholder следующим образом: -

yourTextView.placeholder = "Placeholder" 

extension UITextView :UITextViewDelegate
{

    /// Resize the placeholder when the UITextView bounds change
    override open var bounds: CGRect {
        didSet {
            self.resizePlaceholder()
        }
    }

    /// The UITextView placeholder text
    public var placeholder: String? {
        get {
            var placeholderText: String?

            if let placeholderLabel = self.viewWithTag(100) as? UILabel {
                placeholderText = placeholderLabel.text
            }

            return placeholderText
        }
        set {
            if let placeholderLabel = self.viewWithTag(100) as! UILabel? {
                placeholderLabel.text = newValue
                placeholderLabel.sizeToFit()
            } else {
                self.addPlaceholder(newValue!)
            }
        }
    }

    /// When the UITextView did change, show or hide the label based on if the UITextView is empty or not
    ///
    /// - Parameter textView: The UITextView that got updated
    public func textViewDidChange(_ textView: UITextView) {
        if let placeholderLabel = self.viewWithTag(100) as? UILabel {
            placeholderLabel.isHidden = self.text.characters.count > 0
        }
    }

    /// Resize the placeholder UILabel to make sure it's in the same position as the UITextView text
    private func resizePlaceholder() {
        if let placeholderLabel = self.viewWithTag(100) as! UILabel? {
            let labelX = self.textContainer.lineFragmentPadding
            let labelY = self.textContainerInset.top - 2
            let labelWidth = self.frame.width - (labelX * 2)
            let labelHeight = placeholderLabel.frame.height

            placeholderLabel.frame = CGRect(x: labelX, y: labelY, width: labelWidth, height: labelHeight)
        }
    }

    /// Adds a placeholder UILabel to this UITextView
    private func addPlaceholder(_ placeholderText: String) {
        let placeholderLabel = UILabel()

        placeholderLabel.text = placeholderText
        placeholderLabel.sizeToFit()

        placeholderLabel.font = self.font
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.tag = 100

        placeholderLabel.isHidden = self.text.characters.count > 0

        self.addSubview(placeholderLabel)
        self.resizePlaceholder()
        self.delegate = self
    }
}

1
Работал как чары !! Спасибо тебе большое ! :)
Нахид Райхан

Добро пожаловать, дорогие, всего наилучшего
Сэндип Джилл

15

Я удивлен, что никто не упомянул NSTextStorageDelegate. UITextViewDelegateМетоды будут вызваны только взаимодействием с пользователем, но не программно. Например, когда вы устанавливаете textсвойство текстового представления программно, вам придется самостоятельно устанавливать видимость заполнителя, потому что методы делегата не будут вызываться.

Однако, NSTextStorageDelegate«S textStorage(_:didProcessEditing:range:changeInLength:)метод, вы будете уведомлены о любых изменениях в тексте, даже если это делается программно. Просто назначьте это так:

textView.textStorage.delegate = self

UITextViewэтом свойстве делегата используется nilпо умолчанию, поэтому оно не влияет на поведение по умолчанию.)

Объедините его с UILabelтехникой @clearlight демонстрирует, можно легко обернуть всю UITextView«s placeholderреализации в расширение.

extension UITextView {

    private class PlaceholderLabel: UILabel { }

    private var placeholderLabel: PlaceholderLabel {
        if let label = subviews.compactMap( { $0 as? PlaceholderLabel }).first {
            return label
        } else {
            let label = PlaceholderLabel(frame: .zero)
            label.font = font
            addSubview(label)
            return label
        }
    }

    @IBInspectable
    var placeholder: String {
        get {
            return subviews.compactMap( { $0 as? PlaceholderLabel }).first?.text ?? ""
        }
        set {
            let placeholderLabel = self.placeholderLabel
            placeholderLabel.text = newValue
            placeholderLabel.numberOfLines = 0
            let width = frame.width - textContainer.lineFragmentPadding * 2
            let size = placeholderLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
            placeholderLabel.frame.size.height = size.height
            placeholderLabel.frame.size.width = width
            placeholderLabel.frame.origin = CGPoint(x: textContainer.lineFragmentPadding, y: textContainerInset.top)

            textStorage.delegate = self
        }
    }

}

extension UITextView: NSTextStorageDelegate {

    public func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) {
        if editedMask.contains(.editedCharacters) {
            placeholderLabel.isHidden = !text.isEmpty
        }
    }

}

Обратите внимание, что использование закрытого (вложенного) класса называется PlaceholderLabel. У него вообще нет реализации, но он дает нам способ идентифицировать метку-заполнитель, которая гораздо более «быстрая», чем использование tagсвойства.

При таком подходе вы все равно можете назначить делегата UITextViewкому-либо еще.

Вам даже не нужно менять классы ваших текстовых представлений. Просто добавьте расширение (я), и вы сможете назначить строку-заполнитель каждому UITextViewв вашем проекте, даже в Интерфейсном Разработчике.

Я упустил реализацию placeholderColorсвойства для ясности, но оно может быть реализовано всего за несколько строк с аналогичной вычисляемой переменной placeholder.


Очень элегантное решение. Я люблю это.
Дейв Баттон

textView.textStorage.delegate = selfэто в контроллере представления потребует, чтобы мы связали тот контроллер представления с NSTextStorageDelegate. Это действительно требует?
Hemang

Просто и работает как шарм. Это должен быть принят ответ
Qaiser Abbas

@ Hemang - это текстовое представление NSTextStorageDelegate, а не контроллер представления.
yesleon

14

Я сделал это с помощью двух разных текстовых представлений:

  1. Один в фоновом режиме, который используется в качестве заполнителя.
  2. Один на переднем плане (с прозрачным фоном), который пользователь фактически вводит.

Идея состоит в том, что, как только пользователь начинает набирать текст на переднем плане, заполнитель на заднем плане исчезает (и появляется снова, если пользователь удаляет все). Таким образом, он ведет себя как заполнитель для однострочного текстового поля.

Вот код, который я использовал для этого. Обратите внимание, что descriptionField - это поле, которое вводит пользователь, а descriptionPlaceholder - это поле в фоновом режиме.

func textViewDidChange(descriptionField: UITextView) {
    if descriptionField.text.isEmpty == false {
        descriptionPlaceholder.text = ""
    } else {
        descriptionPlaceholder.text = descriptionPlaceholderText
    }
}

1
Этот способ немного хакерский, но он намного проще и дает именно те результаты, которые вы хотите. Хорошая идея
Уильям Т.

9

Основываясь на некоторых замечательных предложениях, я смог собрать следующий легкий подкласс, совместимый с Interface-Builder UITextView, который:

  • Включает настраиваемый текст-заполнитель, стилизованный под стиль UITextField.
  • Не требует никаких дополнительных подпредставлений или ограничений.
  • Не требует какого-либо делегирования или другого поведения от ViewController.
  • Не требует никаких уведомлений.
  • Сохраняет этот текст полностью отделенным от любых внешних классов, просматривая textсвойство поля .

Любые предложения по улучшению приветствуются, особенно если есть какой-либо способ получить цвет заполнителя iOS программно, а не жестко программировать его.

Swift v5:

import UIKit
@IBDesignable class TextViewWithPlaceholder: UITextView {

    override var text: String! { // Ensures that the placeholder text is never returned as the field's text
        get {
            if showingPlaceholder {
                return "" // When showing the placeholder, there's no real text to return
            } else { return super.text }
        }
        set { super.text = newValue }
    }
    @IBInspectable var placeholderText: String = ""
    @IBInspectable var placeholderTextColor: UIColor = UIColor(red: 0.78, green: 0.78, blue: 0.80, alpha: 1.0) // Standard iOS placeholder color (#C7C7CD). See /programming/31057746/whats-the-default-color-for-placeholder-text-in-uitextfield
    private var showingPlaceholder: Bool = true // Keeps track of whether the field is currently showing a placeholder

    override func didMoveToWindow() {
        super.didMoveToWindow()
        if text.isEmpty {
            showPlaceholderText() // Load up the placeholder text when first appearing, but not if coming back to a view where text was already entered
        }
    }

    override func becomeFirstResponder() -> Bool {
        // If the current text is the placeholder, remove it
        if showingPlaceholder {
            text = nil
            textColor = nil // Put the text back to the default, unmodified color
            showingPlaceholder = false
        }
        return super.becomeFirstResponder()
    }

    override func resignFirstResponder() -> Bool {
        // If there's no text, put the placeholder back
        if text.isEmpty {
            showPlaceholderText()
        }
        return super.resignFirstResponder()
    }

    private func showPlaceholderText() {
        showingPlaceholder = true
        textColor = placeholderTextColor
        text = placeholderText
    }
}

6

УСТАНОВИТЬ значение в представлении нагрузки

    txtVw!.autocorrectionType = UITextAutocorrectionType.No
    txtVw!.text = "Write your Placeholder"
    txtVw!.textColor = UIColor.lightGrayColor()



func textViewDidBeginEditing(textView: UITextView) {
    if (txtVw?.text == "Write your Placeholder")

    {
        txtVw!.text = nil
        txtVw!.textColor = UIColor.blackColor()
    }
}

func textViewDidEndEditing(textView: UITextView) {
    if txtVw!.text.isEmpty
    {
        txtVw!.text = "Write your Placeholder"
        txtVw!.textColor = UIColor.lightGrayColor()
    }
    textView.resignFirstResponder()
}

6

Я пытался сделать код удобным из Clearlight «s ответа .

extension UITextView{

    func setPlaceholder() {

        let placeholderLabel = UILabel()
        placeholderLabel.text = "Enter some text..."
        placeholderLabel.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        placeholderLabel.tag = 222
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !self.text.isEmpty

        self.addSubview(placeholderLabel)
    }

    func checkPlaceholder() {
        let placeholderLabel = self.viewWithTag(222) as! UILabel
        placeholderLabel.isHidden = !self.text.isEmpty
    }

}

использование

override func viewDidLoad() {
    textView.delegate = self
    textView.setPlaceholder()
}

func textViewDidChange(_ textView: UITextView) {
    textView.checkPlaceholder()
}

Пара вопросов. (1) Как расширение, которое принимает и «заимствует» значение свойства тега UIView, существует риск того, что кто-то может использовать этот тег в своей иерархии представления, не подозревая об использовании расширения, создавая чрезвычайно сложную для диагностики ошибку. Подобные вещи не входят в библиотечный код или расширения. (2) Требуется, чтобы вызывающий абонент объявил делегата. Плавающий Заполнитель избегает хаки, имеет крошечный след, просто и полностью локальные, что делает его безопасной ставкой.
ясный свет

5

Еще одно решение (Swift 3):

import UIKit

protocol PlaceholderTextViewDelegate {
    func placeholderTextViewDidChangeText(_ text:String)
    func placeholderTextViewDidEndEditing(_ text:String)
}

final class PlaceholderTextView: UITextView {

    var notifier:PlaceholderTextViewDelegate?

    var placeholder: String? {
        didSet {
            placeholderLabel?.text = placeholder
        }
    }
    var placeholderColor = UIColor.lightGray
    var placeholderFont = UIFont.appMainFontForSize(14.0) {
        didSet {
            placeholderLabel?.font = placeholderFont
        }
    }

    fileprivate var placeholderLabel: UILabel?

    // MARK: - LifeCycle

    init() {
        super.init(frame: CGRect.zero, textContainer: nil)
        awakeFromNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        self.delegate = self
        NotificationCenter.default.addObserver(self, selector: #selector(PlaceholderTextView.textDidChangeHandler(notification:)), name: .UITextViewTextDidChange, object: nil)

        placeholderLabel = UILabel()
        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.text = placeholder
        placeholderLabel?.textAlignment = .left
        placeholderLabel?.numberOfLines = 0
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        placeholderLabel?.font = placeholderFont

        var height:CGFloat = placeholderFont.lineHeight
        if let data = placeholderLabel?.text {

            let expectedDefaultWidth:CGFloat = bounds.size.width
            let fontSize:CGFloat = placeholderFont.pointSize

            let textView = UITextView()
            textView.text = data
            textView.font = UIFont.appMainFontForSize(fontSize)
            let sizeForTextView = textView.sizeThatFits(CGSize(width: expectedDefaultWidth,
                                                               height: CGFloat.greatestFiniteMagnitude))
            let expectedTextViewHeight = sizeForTextView.height

            if expectedTextViewHeight > height {
                height = expectedTextViewHeight
            }
        }

        placeholderLabel?.frame = CGRect(x: 5, y: 0, width: bounds.size.width - 16, height: height)

        if text.isEmpty {
            addSubview(placeholderLabel!)
            bringSubview(toFront: placeholderLabel!)
        } else {
            placeholderLabel?.removeFromSuperview()
        }
    }

    func textDidChangeHandler(notification: Notification) {
        layoutSubviews()
    }

}

extension PlaceholderTextView : UITextViewDelegate {
    // MARK: - UITextViewDelegate
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if(text == "\n") {
            textView.resignFirstResponder()
            return false
        }
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        notifier?.placeholderTextViewDidChangeText(textView.text)
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        notifier?.placeholderTextViewDidEndEditing(textView.text)
    }
}

результат

введите описание изображения здесь


4

Простое и быстрое решение, которое работает для меня:

@IBDesignable
class PlaceHolderTextView: UITextView {

    @IBInspectable var placeholder: String = "" {
         didSet{
             updatePlaceHolder()
        }
    }

    @IBInspectable var placeholderColor: UIColor = UIColor.gray {
        didSet {
            updatePlaceHolder()
        }
    }

    private var originalTextColor = UIColor.darkText
    private var originalText: String = ""

    private func updatePlaceHolder() {

        if self.text == "" || self.text == placeholder  {

            self.text = placeholder
            self.textColor = placeholderColor
            if let color = self.textColor {

                self.originalTextColor = color
            }
            self.originalText = ""
        } else {
            self.textColor = self.originalTextColor
            self.originalText = self.text
        }

    }

    override func becomeFirstResponder() -> Bool {
        let result = super.becomeFirstResponder()
        self.text = self.originalText
        self.textColor = self.originalTextColor
        return result
    }
    override func resignFirstResponder() -> Bool {
        let result = super.resignFirstResponder()
        updatePlaceHolder()

        return result
    }
}

4

Вот что я использую для этой работы.

@IBDesignable class UIPlaceholderTextView: UITextView {

    var placeholderLabel: UILabel?

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        sharedInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        sharedInit()
    }

    override func prepareForInterfaceBuilder() {
        sharedInit()
    }

    func sharedInit() {
        refreshPlaceholder()
        NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: UITextView.textDidChangeNotification, object: nil)
    }

    @IBInspectable var placeholder: String? {
        didSet {
            refreshPlaceholder()
        }
    }

    @IBInspectable var placeholderColor: UIColor? = .darkGray {
        didSet {
            refreshPlaceholder()
        }
    }

    @IBInspectable var placeholderFontSize: CGFloat = 14 {
        didSet {
            refreshPlaceholder()
        }
    }

    func refreshPlaceholder() {
        if placeholderLabel == nil {
            placeholderLabel = UILabel()
            let contentView = self.subviews.first ?? self

            contentView.addSubview(placeholderLabel!)
            placeholderLabel?.translatesAutoresizingMaskIntoConstraints = false

            placeholderLabel?.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: textContainerInset.left + 4).isActive = true
            placeholderLabel?.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: textContainerInset.right + 4).isActive = true
            placeholderLabel?.topAnchor.constraint(equalTo: contentView.topAnchor, constant: textContainerInset.top).isActive = true
            placeholderLabel?.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: textContainerInset.bottom)
        }
        placeholderLabel?.text = placeholder
        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.font = UIFont.systemFont(ofSize: placeholderFontSize)
    }

    @objc func textChanged() {
        if self.placeholder?.isEmpty ?? true {
            return
        }

        UIView.animate(withDuration: 0.25) {
            if self.text.isEmpty {
                self.placeholderLabel?.alpha = 1.0
            } else {
                self.placeholderLabel?.alpha = 0.0
            }
        }
    }

    override var text: String! {
        didSet {
            textChanged()
        }
    }

}

Я знаю, что есть несколько подходов, подобных этому, но преимущества этого:

  • Можно установить заполнитель текста, размер шрифта и цвет в IB .
  • Больше не отображается предупреждение « Scroll View имеет неоднозначный прокручиваемый контент » в IB.
  • Добавить анимацию, чтобы показать / скрыть заполнитель.

3

Swift 3.2

extension EditProfileVC:UITextViewDelegate{

    func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.textColor == UIColor.lightGray {
            textView.text = nil
            textView.textColor = UIColor.black
       }
    }
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.text = "Placeholder"
            textView.textColor = UIColor.lightGray
        }
    }
}

Сначала, когда пользователь начинает редактировать вызов textViewDidBeginEditing, а затем проверяет, означает ли цвет текста серый, означает, что пользователь ничего не писал, тогда установите значение textview nil и измените цвет на черный для текстовых сообщений пользователя.

Когда пользователь завершает редактирование textViewDidEndEditing, вызывается и проверяется, не пишет ли пользователь ничего в textview, тогда текст устанавливается серым цветом с текстом «PlaceHolder»


3

Вот мой способ решения этой проблемы ( Swift 4 ):

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

import UIKit

class PlaceholderTextView: UITextView {
    var placeholderColor: UIColor = .lightGray
    var defaultTextColor: UIColor = .black

    private var isShowingPlaceholder = false {
        didSet {
            if isShowingPlaceholder {
                text = placeholder
                textColor = placeholderColor
            } else {
                textColor = defaultTextColor
            }
        }
    }

    var placeholder: String? {
        didSet {
            isShowingPlaceholder = !hasText
        }
    }

    @objc private func textViewDidBeginEditing(notification: Notification) {
        textColor = defaultTextColor
        if isShowingPlaceholder { text = nil }
    }

    @objc private func textViewDidEndEditing(notification: Notification) {
        isShowingPlaceholder = !hasText
    }

    // MARK: - Construction -
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private func setup() {
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidBeginEditing(notification:)), name: UITextView.textDidBeginEditingNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidEndEditing(notification:)), name: UITextView.textDidEndEditingNotification, object: nil)
    }

    // MARK: - Destruction -
    deinit { NotificationCenter.default.removeObserver(self) }
}

Это отличное и простое решение.
Джаред

2

Я не знаю, почему люди слишком усложняют эту проблему ... Это довольно просто и понятно. Вот подкласс UITextView, который обеспечивает запрошенную функциональность.

- (void)customInit
{
    self.contentMode = UIViewContentModeRedraw;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}

    - (void)textChanged:(NSNotification *)notification
    {
        if (notification.object == self) {
            if(self.textStorage.length != 0 || !self.textStorage.length) {
                [self setNeedsDisplay];
            }
        }
    }


    #pragma mark - Setters

    - (void)setPlaceholderText:(NSString *)placeholderText withFont:(UIFont *)font
    {
        self.placeholderText = placeholderText;
        self.placeholderTextFont = font;

    }



    - (void)drawRect:(CGRect)rect
    {
        [super drawRect:rect];
        [[UIColor lightGrayColor] setFill];

        if (self.textStorage.length != 0) {
            return;
        }

        CGRect inset = CGRectInset(rect, 8, 8);//Default rect insets for textView
        NSDictionary *attributes =  @{NSFontAttributeName: self.placeholderTextFont, NSForegroundColorAttributeName: [UIColor grayColor]};
        [self.placeholderText drawInRect:inset withAttributes:attributes];
    }`

К вашему сведению, до того, как кто-то меня опередит ... это довольно просто перевести на Свифт. Ничего сложного здесь нет.
TheCodingArt

2

Это мое готовое решение, если вы работаете с несколькими текстовыми представлениями.

func textViewShouldBeginEditing(textView: UITextView) -> Bool {        
    // Set cursor to the beginning if placeholder is set
    if textView.textColor == UIColor.lightGrayColor() {
        textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
    }

    return true
}

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    // Remove placeholder
    if textView.textColor == UIColor.lightGrayColor() && text.characters.count > 0 {
        textView.text = ""
        textView.textColor = UIColor.blackColor()
    }

    if text == "\n" {
        textView.resignFirstResponder()
        return false
    }

    return true
}

func textViewDidChange(textView: UITextView) {
    // Set placeholder if text is empty
    if textView.text.isEmpty {
        textView.text = NSLocalizedString("Hint", comment: "hint")
        textView.textColor = UIColor.lightGrayColor()
        textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
    }
}

func textViewDidChangeSelection(textView: UITextView) {
    // Set cursor to the beginning if placeholder is set
    let firstPosition = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)

    // Do not change position recursively
    if textView.textColor == UIColor.lightGrayColor() && textView.selectedTextRange != firstPosition {
        textView.selectedTextRange = firstPosition
    }
}

Очень мило!
KevinVuD

2

Swift 3.1

Это расширение хорошо сработало для меня: https://github.com/devxoul/UITextView-Placeholder

Вот фрагмент кода:

Установите его через модуль:

pod 'UITextView+Placeholder', '~> 1.2'

Импортируйте его в свой класс

import UITextView_Placeholder

И добавьте placeholderсвойство к уже созданномуUITextView

textView.placeholder = "Put some detail"

Вот так ... Вот как это выглядит (Третья коробка UITextView) введите описание изображения здесь


2

В отличие от почти каждый ответ на этот пост, UITextView делает имеют свойство заполнитель. По причинам вне моего понимания, это раскрывается только в IB, как таковой:

<userDefinedRuntimeAttributes>
  <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="My Placeholder"/>
</userDefinedRuntimeAttributes>

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

Вы также можете установить это свойство в коде так:

textView.setValue("My Placeholder", forKeyPath: "placeholder")

Его мутный , как к погоде это доступ через частный API, как свойство является подвергается.

Я не пытался отправить с этим методом. Но я отправлю этот способ в ближайшее время и обновлю этот ответ соответственно.

ОБНОВИТЬ:

Я отправил этот код в нескольких выпусках без проблем от Apple.

ОБНОВЛЕНИЕ: Это будет работать только в Xcode pre 11.2


в Swift 5 вы можете написать myTextView, placeholder = "введите цвет глаз"
user462990

@ user462990 Можете ли вы предоставить ссылку на документацию для этого? Я не верю, что это точно.
Алекс Чейз

извините, не нашел dox, но это действительно легко проверить ... например "alert.addTextField {(textField3) в textField3.placeholder = language.JBLocalize (фраза:" yourName ")}. jbLocalize - это менеджер переводов, который возвращает Строка
user462990

4
@ user462990 Вы ссылаетесь на UITextFieldНЕ, UITextView пожалуйста, прочитайте вопросы / ответы более внимательно.
Алекс Чейз

3
Отличное решение, но у меня не работает. Каким-то образом это всегда вылетает. Не могли бы вы указать целевую версию развертывания, swift и версии xCode, которые вы используете?
Т. Пасичник

1

В ios нет такого свойства для добавления заполнителя непосредственно в TextView, вместо этого вы можете добавить метку и показать / скрыть изменение в textView. SWIFT 2.0 и убедитесь, что для реализации textviewdelegate

func textViewDidChange(TextView: UITextView)
{

 if  txtShortDescription.text == ""
    {
        self.lblShortDescription.hidden = false
    }
    else
    {
        self.lblShortDescription.hidden = true
    }

}

1

Swift - я написал класс, который унаследовал UITextView, и добавил UILabel в качестве подпредставления, чтобы действовать в качестве заполнителя.

  import UIKit
  @IBDesignable
  class HintedTextView: UITextView {

      @IBInspectable var hintText: String = "hintText" {
          didSet{
              hintLabel.text = hintText
          }
      }

      private lazy var hintLabel: UILabel = {
          let label = UILabel()
          label.font = UIFont.systemFontOfSize(16)
          label.textColor = UIColor.lightGrayColor()
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()


      override init(frame: CGRect, textContainer: NSTextContainer?) {
          super.init(frame: frame, textContainer: textContainer)
          setupView()
      }

      required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
         setupView()
      }

      override func prepareForInterfaceBuilder() {
         super.prepareForInterfaceBuilder()
         setupView()
      }

      private func setupView() {

        translatesAutoresizingMaskIntoConstraints = false
        delegate = self
        font = UIFont.systemFontOfSize(16)

        addSubview(hintLabel)

        NSLayoutConstraint.activateConstraints([

           hintLabel.leftAnchor.constraintEqualToAnchor(leftAnchor, constant: 4),
           hintLabel.rightAnchor.constraintEqualToAnchor(rightAnchor, constant: 8),
           hintLabel.topAnchor.constraintEqualToAnchor(topAnchor, constant: 4),
           hintLabel.heightAnchor.constraintEqualToConstant(30)
         ])
        }

      override func layoutSubviews() {
        super.layoutSubviews()
        setupView()
     }

}

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

1

Мне нравится решение @ nerdist. Исходя из этого, я создал расширение для UITextView:

import Foundation
import UIKit

extension UITextView
{
  private func add(_ placeholder: UILabel) {
    for view in self.subviews {
        if let lbl = view as? UILabel  {
            if lbl.text == placeholder.text {
                lbl.removeFromSuperview()
            }
        }
    }
    self.addSubview(placeholder)
  }

  func addPlaceholder(_ placeholder: UILabel?) {
    if let ph = placeholder {
      ph.numberOfLines = 0  // support for multiple lines
      ph.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
      ph.sizeToFit()
      self.add(ph)
      ph.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
      ph.textColor = UIColor(white: 0, alpha: 0.3)
      updateVisibility(ph)
    }
  }

  func updateVisibility(_ placeHolder: UILabel?) {
    if let ph = placeHolder {
      ph.isHidden = !self.text.isEmpty
    }
  }
}

Например, в классе ViewController я так его использую:

class MyViewController: UIViewController, UITextViewDelegate {
  private var notePlaceholder: UILabel!
  @IBOutlet weak var txtNote: UITextView!
  ...
  // UIViewController
  override func viewDidLoad() {
    notePlaceholder = UILabel()
    notePlaceholder.text = "title\nsubtitle\nmore..."
    txtNote.addPlaceholder(notePlaceholder)
    ...
  }

  // UITextViewDelegate
  func textViewDidChange(_ textView: UITextView) {
    txtNote.updateVisbility(notePlaceholder)
    ...
  }

Заполнитель на UITextview!

введите описание изображения здесь

ОБНОВИТЬ :

Если вы изменяете текст textview в коде, не забудьте вызвать метод updateVisibitly, чтобы скрыть заполнитель:

txtNote.text = "something in code"
txtNote.updateVisibility(self.notePlaceholder) // hide placeholder if text is not empty.

Чтобы запретить добавление заполнителя более одного раза, add()в extension.


Еще раз спасибо за ваше улучшение до оригинала. Я потратил слишком много времени на эти выходные, но я думаю, что вам понравится экстремальный вариант EZ Placeholder, который я в конце концов придумала: stackoverflow.com/a/41081244/2079103
clearlight

1

В swift2.2:

public class CustomTextView: UITextView {

private struct Constants {
    static let defaultiOSPlaceholderColor = UIColor(red: 0.0, green: 0.0, blue: 0.0980392, alpha: 0.22)
}
private let placeholderLabel: UILabel = UILabel()

private var placeholderLabelConstraints = [NSLayoutConstraint]()

@IBInspectable public var placeholder: String = "" {
    didSet {
        placeholderLabel.text = placeholder
    }
}

@IBInspectable public var placeholderColor: UIColor = CustomTextView.Constants.defaultiOSPlaceholderColor {
    didSet {
        placeholderLabel.textColor = placeholderColor
    }
}

override public var font: UIFont! {
    didSet {
        placeholderLabel.font = font
    }
}

override public var textAlignment: NSTextAlignment {
    didSet {
        placeholderLabel.textAlignment = textAlignment
    }
}

override public var text: String! {
    didSet {
        textDidChange()
    }
}

override public var attributedText: NSAttributedString! {
    didSet {
        textDidChange()
    }
}

override public var textContainerInset: UIEdgeInsets {
    didSet {
        updateConstraintsForPlaceholderLabel()
    }
}

override public init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    commonInit()
}

required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

private func commonInit() {
    NSNotificationCenter.defaultCenter().addObserver(self,
                                                     selector: #selector(textDidChange),
                                                     name: UITextViewTextDidChangeNotification,
                                                     object: nil)

    placeholderLabel.font = font
    placeholderLabel.textColor = placeholderColor
    placeholderLabel.textAlignment = textAlignment
    placeholderLabel.text = placeholder
    placeholderLabel.numberOfLines = 0
    placeholderLabel.backgroundColor = UIColor.clearColor()
    placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
    addSubview(placeholderLabel)
    updateConstraintsForPlaceholderLabel()
}

private func updateConstraintsForPlaceholderLabel() {
    var newConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
                                                                        options: [],
                                                                        metrics: nil,
                                                                        views: ["placeholder": placeholderLabel])
    newConstraints += NSLayoutConstraint.constraintsWithVisualFormat("V:|-(\(textContainerInset.top))-[placeholder]",
                                                                     options: [],
                                                                     metrics: nil,
                                                                     views: ["placeholder": placeholderLabel])
    newConstraints.append(NSLayoutConstraint(
        item: placeholderLabel,
        attribute: .Width,
        relatedBy: .Equal,
        toItem: self,
        attribute: .Width,
        multiplier: 1.0,
        constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0)
        ))
    removeConstraints(placeholderLabelConstraints)
    addConstraints(newConstraints)
    placeholderLabelConstraints = newConstraints
}

@objc private func textDidChange() {
    placeholderLabel.hidden = !text.isEmpty
}

public override func layoutSubviews() {
    super.layoutSubviews()
    placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self,
                                                        name: UITextViewTextDidChangeNotification,
                                                        object: nil)
}

}

В swift3:

import UIKit

Класс CustomTextView: UITextView {

private struct Constants {
    static let defaultiOSPlaceholderColor = UIColor(red: 0.0, green: 0.0, blue: 0.0980392, alpha: 0.22)
}
private let placeholderLabel: UILabel = UILabel()

private var placeholderLabelConstraints = [NSLayoutConstraint]()

@IBInspectable public var placeholder: String = "" {
    didSet {
        placeholderLabel.text = placeholder
    }
}

@IBInspectable public var placeholderColor: UIColor = CustomTextView.Constants.defaultiOSPlaceholderColor {
    didSet {
        placeholderLabel.textColor = placeholderColor
    }
}

override public var font: UIFont! {
    didSet {
        placeholderLabel.font = font
    }
}

override public var textAlignment: NSTextAlignment {
    didSet {
        placeholderLabel.textAlignment = textAlignment
    }
}

override public var text: String! {
    didSet {
        textDidChange()
    }
}

override public var attributedText: NSAttributedString! {
    didSet {
        textDidChange()
    }
}

override public var textContainerInset: UIEdgeInsets {
    didSet {
        updateConstraintsForPlaceholderLabel()
    }
}

override public init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    commonInit()
}

required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

private func commonInit() {
    NotificationCenter.default.addObserver(self,
                                                     selector: #selector(textDidChange),
                                                     name: NSNotification.Name.UITextViewTextDidChange,
                                                     object: nil)

    placeholderLabel.font = font
    placeholderLabel.textColor = placeholderColor
    placeholderLabel.textAlignment = textAlignment
    placeholderLabel.text = placeholder
    placeholderLabel.numberOfLines = 0
    placeholderLabel.backgroundColor = UIColor.clear
    placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
    addSubview(placeholderLabel)
    updateConstraintsForPlaceholderLabel()
}

private func updateConstraintsForPlaceholderLabel() {
    var newConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
        options: [],
        metrics: nil,
        views: ["placeholder": placeholderLabel])
    newConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-(\(textContainerInset.top))-[placeholder]",
        options: [],
        metrics: nil,
        views: ["placeholder": placeholderLabel])
    newConstraints.append(NSLayoutConstraint(
        item: placeholderLabel,
        attribute: .width,
        relatedBy: .equal,
        toItem: self,
        attribute: .width,
        multiplier: 1.0,
        constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0)
    ))
    removeConstraints(placeholderLabelConstraints)
    addConstraints(newConstraints)
    placeholderLabelConstraints = newConstraints
}

@objc private func textDidChange() {
    placeholderLabel.isHidden = !text.isEmpty
}

public override func layoutSubviews() {
    super.layoutSubviews()
    placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}

deinit {
    NotificationCenter.default.removeObserver(self,
                                                        name: NSNotification.Name.UITextViewTextDidChange,
                                                        object: nil)
}

}

Я написал класс по быстрому. Вам нужно импортировать этот класс, когда это необходимо.


Пожалуйста, укажите, для какой версии Swift это (не работает для Swift 3)
clearlight

1

Я не могу добавить комментарий из-за репутации. добавьте еще одного делегата в ответ @clearlight.

func textViewDidBeginEditing(_ textView: UITextView) { 
        cell.placeholderLabel.isHidden = !textView.text.isEmpty
}

это нужно

потому что textViewDidChangeне называется первый раз


1

Нет, там нет места для текста. Вы должны поставить метку над ним, когда пользователь вводит текстовое представление, затем скрыть его или установить значение по умолчанию, когда пользователь вводит, удалить все значения.


1

func setPlaceholder(){
var placeholderLabel = UILabel()
        placeholderLabel.text = "Describe your need..."
        placeholderLabel.font = UIFont.init(name: "Lato-Regular", size: 15.0) ?? UIFont.boldSystemFont(ofSize: 14.0)
        placeholderLabel.sizeToFit()
        descriptionTextView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (descriptionTextView.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
}



//Delegate Method.

func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
	


1

Мне пришлось отправить очередь, чтобы текст заполнителя снова появился после завершения редактирования.

func textViewDidBeginEditing(_ textView: UITextView) {

    if textView.text == "Description" {
        textView.text = nil
    }
}

func textViewDidEndEditing(_ textView: UITextView) {

    if textView.text.isEmpty {
        DispatchQueue.main.async {
            textView.text = "Description"
        }
    }
}

Что я наберу вместо последней строки ”textView.text =” Description ”, если я хочу, чтобы это значение было тем, что печатает пользователь?
МКС

1

Swift:

Добавьте свой TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

В viewWillAppearметод добавьте следующее:

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

    txtViewMessage.text = "Place Holder Name"
    txtViewMessage.textColor = UIColor.lightGray

}

Пожалуйста, добавьте Delegateрасширение Using (UITextViewDelegate):

// MARK: - UITextViewDelegate
extension ViewController: UITextViewDelegate
{

    func textViewDidBeginEditing(_ textView: UITextView)
    {

        if !txtViewMessage.text!.isEmpty && txtViewMessage.text! == "Place Holder Name"
        {
            txtViewMessage.text = ""
            txtViewMessage.textColor = UIColor.black
        }
    }

    func textViewDidEndEditing(_ textView: UITextView)
    {

        if txtViewMessage.text.isEmpty
        {
            txtViewMessage.text = "Place Holder Name"
            txtViewMessage.textColor = UIColor.lightGray
        }
    }
}

1

Наше решение позволяет избежать использования свойств UITextView textи textColorсвойств, что удобно, если вы ведете счетчик символов.

Это просто:

1) Создайте пустышку UITextViewв раскадровке с теми же свойствами, что и у мастера UITextView. Присвойте замещающий текст пустому тексту.

2) Совместите верхний, левый и правый края двух UITextViews.

3) Поместите манекен позади мастера.

4) Переопределите textViewDidChange(textView:)функцию делегата мастера и покажите манекен, если мастер имеет 0 символов. В противном случае покажи мастера.

Это предполагает, что оба UITextViewsимеют прозрачный фон. Если это не так, поместите манекен сверху, когда есть 0 символов, и вставьте его внизу, если> 0 символов. Вам также придется поменять местами респонденты, чтобы убедиться, что курсор следует вправо UITextView.


1

Свифт 4, 4.2 и 5

[![@IBOutlet var detailTextView: UITextView!

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

extension ContactUsViewController : UITextViewDelegate {

    public func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.text == "Write your message here..." {
            detailTextView.text = ""
            detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.86)

        }
        textView.becomeFirstResponder()
    }

    public func textViewDidEndEditing(_ textView: UITextView) {

        if textView.text == "" {
            detailTextView.text = "Write your message here..."
            detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.30)
        }
        textView.resignFirstResponder()
    }
[![}][1]][1]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.