411 Stimmen

Füge Platzhaltertext in UITextView in Swift ein?

Wie kann ich einen Platzhalter in einem UITextView hinzufügen, ähnlich wie bei UITextField, in Swift?

809voto

Lyndsey Scott Punkte 36645

Aktualisiert für Swift 4

UITextView hat standardmäßig keine Placeholder-Eigenschaft, daher müssten Sie eine programmatisch mithilfe der Methoden des UITextViewDelegate erstellen und manipulieren. Ich empfehle die Verwendung von Lösung #1 oder #2 je nach gewünschtem Verhalten.

Hinweis: Fügen Sie für beide Lösungen UITextViewDelegate zur Klasse hinzu und setzen Sie textView.delegate = self, um die Delegate-Methoden des Textfelds zu verwenden.


Lösung #1 - Wenn Sie möchten, dass der Placeholder verschwindet, sobald der Benutzer das Textfeld auswählt:

Legen Sie zuerst den UITextView fest, um den Placeholder-Text zu enthalten und setzen Sie ihn auf eine hellgraue Farbe, um das Aussehen des Placeholder-Texts eines UITextField nachzuahmen. Tun Sie dies entweder im viewDidLoad oder beim Erstellen des Textfelds.

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

Dann, wenn der Benutzer damit beginnt, den Text im Textfeld zu bearbeiten, löschen Sie den Placeholder-Text und setzen Sie die Textfarbe auf Schwarz, um die Eingabe des Benutzers anzupassen, falls der Textfarbe des Textfelds hellgrau ist (also wenn es einen Placeholder enthält).

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

Wenn der Benutzer die Bearbeitung des Textfelds beendet und es nicht mehr als erstes ausgezeichnet ist, setzen Sie den Platzhalter zurück, indem Sie den Platzhaltertext erneut hinzufügen und seine Farbe auf hellgrau setzen, wenn das Textfeld leer ist.

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

Lösung #2 - Wenn der Placeholder angezeigt werden soll, wenn das Textfeld leer ist, auch wenn das Textfeld ausgewählt ist:

Legen Sie zuerst den Placeholder im viewDidLoad fest:

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

textView.becomeFirstResponder()

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

(Hinweis: Da der OP das Textfeld sofort ausgewählt haben wollte, habe ich die Textfeldauswahl in den obigen Code integriert. Wenn dies nicht Ihrem gewünschten Verhalten entspricht und Sie nicht möchten, dass das Textfeld beim Laden der Ansicht ausgewählt wird, entfernen Sie die letzten beiden Zeilen aus dem obigen Codeblock.)

Verwenden Sie dann die Methode shouldChangeTextInRange des UITextViewDelegate wie folgt:

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

    // Kombinieren Sie den aktuellen Text des Textfelds und den Ersatztext, um den aktualisierten Textstring zu erstellen
    let currentText:String = textView.text
    let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)

    // Wenn der aktualisierte Text leer ist, fügen Sie den Platzhalter hinzu und setzen den Cursor an den Anfang des Textfelds
    if updatedText.isEmpty {

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

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

    // Andernfalls, wenn der Placeholder des Textfelds angezeigt wird und die Länge des Ersatztexts größer als 0 ist, setzen Sie die Textfarbe auf Schwarz und den Text auf den Ersatztext
     else if textView.textColor == UIColor.lightGray && !text.isEmpty {
        textView.textColor = UIColor.black
        textView.text = text
    }

    // In allen anderen Fällen sollte der Text mit dem üblichen Verhalten geändert werden...
    else {
        return true
    }

    // ...geben Sie andernfalls false zurück, da die Aktualisierungen bereits erfolgt sind
    return false
}

Und implementieren Sie auch textViewDidChangeSelection, um zu verhindern, dass der Benutzer die Position des Cursors ändert, während der Placeholder sichtbar ist. (Hinweis: textViewDidChangeSelection wird aufgerufen, bevor die Ansicht geladen wird, also überprüfen Sie nur die Farbe des Textfelds, wenn das Fenster sichtbar ist):

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

329voto

clearlight Punkte 11235

Schwebender Platzhalter


Es ist einfach, sicher und zuverlässig, einen Platzhalter über einem Textfeld zu positionieren, seine Schriftart, Farbe festzulegen und die Sichtbarkeit des Platzhalters zu verwalten, indem Änderungen der Zeichenanzahl im Textfeld verfolgt werden.

<i>Update: Eingearbeitete Vorschläge von @RakshithaMurangaRodrigo in Kommentar vom 10. Feb '23</i>

Swift 5:

class NotesViewController : UIViewController {

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

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.delegate = self
        placeholderLabel = UILabel()
        placeholderLabel.text = "Geben Sie etwas Text ein..."
        placeholderLabel.font = .italicSystemFont(ofSize: (textView.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        textView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (textView.font?.pointSize)! / 2)
        placeholderLabel.textColor = .tertiaryLabel
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
}

extension NotesViewController : UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        placeholderLabel?.isHidden = !textView.text.isEmpty
    }
    func textViewDidEndEditing(_ textView: UITextView) {         
        placeholderLabel?.isHidden = !textView.text.isEmpty     
    }  
    func textViewDidBeginEditing(_ textView: UITextView) {         
        placeholderLabel?.isHidden = true     
    }
}

38voto

Juan Boero Punkte 5900

Swift:

Fügen Sie Ihren Text anzeigen programmatisch oder über den Interface Builder hinzu. Erstellen Sie dazu, falls letzteres der Fall ist, den Outlet:

@IBOutlet weak var yourTextView: UITextView!

Fügen Sie bitte das Delegate hinzu (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

Fügen Sie im Method viewDidLoad Folgendes hinzu:

override func viewDidLoad() {
        super.viewDidLoad()
        // Führen Sie alle zusätzlichen Einrichtungen nach dem Laden der Ansicht durch, in der Regel von einer Nib-Datei.

    yourTextView.delegate = self
    yourTextView.text = "Platzhaltertext hier eingeben..."
    yourTextView.textColor = UIColor.lightGray

Lassen Sie mich jetzt den magischen Teil vorstellen, fügen Sie diese Funktion hinzu:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Beachten Sie, dass dies jedes Mal ausgeführt wird, wenn die Bearbeitung beginnt. Hier überprüfen wir Bedingungen, um den Status anhand der Farbeigenschaft zu bestimmen. Das Setzen des Textes auf nil wird nicht empfohlen. Direkt danach setzen wir die Textfarbe auf gewünschte Weise, in diesem Fall auf Schwarz.

Fügen Sie nun auch diese Funktion hinzu:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Ich betone nochmals: Vergleichen Sie nicht mit nil, ich habe das bereits ausprobiert und es würde nicht funktionieren. Anschließend setzen wir die Werte zurück auf den Platzhalterstil und die Farbe zurück auf die Platzhalterfarbe, da dies eine Bedingung ist, die in textViewDidBeginEditing überprüft wird.

34voto

yesleon Punkte 828

Ich bin überrascht, dass niemand NSTextStorageDelegate erwähnt hat. Die Methoden von UITextViewDelegate werden nur durch Benutzerinteraktion, nicht aber programmgesteuert, ausgelöst. Wenn Sie also das text-Attribut eines Textfelds programmgesteuert setzen, müssen Sie die Sichtbarkeit des Platzhalters selbst festlegen, da die Delegate-Methoden nicht aufgerufen werden.

Mit der Methode textStorage(_:didProcessEditing:range:changeInLength:) von NSTextStorageDelegate werden Sie jedoch über jede Änderung am Text benachrichtigt, auch wenn sie programmgesteuert erfolgt. Weisen Sie es einfach wie folgt zu:

textView.textStorage.delegate = self

(Bei UITextView ist dieses Delegate-Attribut standardmäßig nil, daher beeinflusst es kein Standardverhalten.)

In Kombination mit der Technik des Labels, die @clearlight zeigt, kann man die gesamte Implementierung des Platzhalters von UITextView ganz einfach in eine Erweiterung einbetten.

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
        }
    }

}

Beachten Sie die Verwendung einer privaten (verschachtelten) Klasse namens PlaceholderLabel. Sie hat überhaupt keine Implementierung, bietet uns aber eine Möglichkeit, das Platzhalterlabel zu identifizieren, was weit "swifter" ist als die Verwendung des tag-Attributs.

Mit diesem Ansatz können Sie immer noch das Delegate des UITextView an jemand anderen zuweisen.

Sie müssen noch nicht einmal die Klassen Ihrer Textfelder ändern. Fügen Sie einfach die Erweiterung(en) hinzu, und Sie werden in der Lage sein, einem Platzhalter-String für jedes UITextView in Ihrem Projekt zuzuweisen, sogar im Interface Builder.

Ich habe die Implementierung eines placeholderColor-Attributs aus Gründen der Klarheit ausgelassen, aber es kann mit nur wenigen weiteren Zeilen mit einer ähnlichen berechneten Variable wie placeholder implementiert werden.

22voto

TheNeil Punkte 2533

Basierend auf einigen großartigen Vorschlägen hier konnte ich folgende leichte, Interface-Builder-kompatible Unterklasse von UITextView zusammenstellen, die:

  • konfigurierbaren Platzhaltertext enthält, gestaltet genau wie bei UITextField.
  • keine zusätzlichen Unteransichten oder Einschränkungen erfordert.
  • keine Delegation oder anderes Verhalten vom ViewController erfordert.
  • keine Benachrichtigungen erfordert.
  • den Platzhaltertext komplett von externen Klassen trennt, die auf die text-Eigenschaft des Feldes schauen.

Verbesserungsvorschläge sind willkommen, insbesondere wenn es einen Weg gibt, die Platzhalterfarbe von iOS programmgesteuert zu ermitteln, anstatt sie fest zu codieren.

Swift v5:

import UIKit
@IBDesignable class TextViewWithPlaceholder: UITextView {

    override var text: String! { // Stellt sicher, dass der Platzhaltertext niemals als Text des Feldes zurückgegeben wird
        get {
            if showingPlaceholder {
                return "" // Wenn der Platzhalter angezeigt wird, gibt es keinen echten Text, der zurückgegeben werden kann
            } 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 Platzhalterfarbe (#C7C7CD). Siehe https://stackoverflow.com/questions/31057746/whats-the-default-color-for-placeholder-text-in-uitextfield
    private var showingPlaceholder: Bool = true // Behält den Überblick darüber, ob das Feld derzeit einen Platzhalter anzeigt

    override func didMoveToWindow() {
        super.didMoveToWindow()
        if text.isEmpty {
            showPlaceholderText() // Lädt den Platzhaltertext beim ersten Erscheinen, aber nicht wenn man zu einer Ansicht zurückkehrt, in der bereits Text eingegeben wurde
        }
    }

    override func becomeFirstResponder() -> Bool {
        // Wenn der aktuelle Text der Platzhalter ist, entferne ihn
        if showingPlaceholder {
            text = nil
            textColor = nil // Setzt den Text wieder auf die Standard-, unveränderte Farbe zurück
            showingPlaceholder = false
        }
        return super.becomeFirstResponder()
    }

    override func resignFirstResponder() -> Bool {
        // Wenn kein Text vorhanden ist, setze den Platzhalter zurück
        if text.isEmpty {
            showPlaceholderText()
        }
        return super.resignFirstResponder()
    }

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X