Wie kann ich Platzhaltertext in eine UITextView in Swift einfügen?


316

Ich mache eine Anwendung, die a verwendet UITextView. Jetzt möchte ich, dass die Textansicht einen Platzhalter hat, der dem ähnlich ist, den Sie für ein Textfeld festlegen können. Wie würden Sie dies mit Swift erreichen?


Dies ist ein uraltes Problem in der iOS-Entwicklung mit UITextView. Ich habe Unterklassen wie die hier erwähnte geschrieben: stackoverflow.com/a/1704469/1403046 . Der Vorteil besteht darin, dass Sie weiterhin einen Delegaten haben und die Klasse an mehreren Stellen verwenden können, ohne die Logik erneut implementieren zu müssen.
cjwirth

Wie würde ich Ihre Unterklasse verwenden, während ich swift für das Projekt verwende? Verwenden Sie eine Bridge-Datei?
StevenR

Sie können dies tun oder es in Swift erneut implementieren. Der Code in der Antwort ist länger als er wirklich sein muss. Der Hauptpunkt besteht darin, die Beschriftung anzuzeigen / auszublenden, die Sie in der Methode hinzufügen, über die Sie benachrichtigt werden, wenn sich der Text ändert.
cjwirth

Sie können das UIFloatLabelTextView-Beispiel von GitHub verwenden. Dieser Platzhalter wird beim Schreiben oben platziert. Wirklich interessant! github.com/ArtSabintsev/UIFloatLabelTextView
Jayprakash Dubey

2
Ehrlich gesagt ist der einfachste Weg, dies zu erreichen, eine benutzerdefinierte Textansicht zu haben und einfach Platzhaltertext hinzuzufügen, der in die Textansicht gezeichnet wird, wenn kein Text vorhanden ist. Jede andere Antwort war bisher eine weitaus überkomplizierte Version davon, die problematisch ist Zustandsverwaltung (einschließlich falsch positiver
Ergebnisse,

Antworten:


646

Aktualisiert für Swift 4

UITextViewhat von Natur aus keine Platzhalter-Eigenschaft, daher müssten Sie eine programmgesteuert mithilfe von UITextViewDelegateMethoden erstellen und bearbeiten . Ich empfehle, je nach gewünschtem Verhalten entweder die Lösung Nr. 1 oder Nr. 2 zu verwenden.

Hinweis: Fügen Sie für beide Lösungen UITextViewDelegateder Klasse hinzu und legen Sie fest textView.delegate = self, dass die Delegatmethoden der Textansicht verwendet werden sollen.


Lösung 1 - Wenn der Platzhalter verschwinden soll, sobald der Benutzer die Textansicht auswählt:

Stellen Sie zuerst das so ein UITextView, dass es den Platzhaltertext enthält, und setzen Sie es auf eine hellgraue Farbe, um das Aussehen des UITextFieldPlatzhaltertextes eines zu imitieren . Entweder in der viewDidLoadoder bei der Erstellung der Textansicht.

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

Wenn der Benutzer mit der Bearbeitung der Textansicht beginnt und die Textansicht einen Platzhalter enthält (dh wenn seine Textfarbe hellgrau ist), löschen Sie den Platzhaltertext und setzen Sie die Textfarbe auf Schwarz, um die Eingabe des Benutzers zu berücksichtigen.

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

Wenn der Benutzer die Bearbeitung der Textansicht abgeschlossen hat und als Ersthelfer zurückgetreten ist und die Textansicht leer ist, setzen Sie den Platzhalter zurück, indem Sie den Platzhaltertext erneut hinzufügen und seine Farbe auf hellgrau setzen.

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

Lösung 2 - Wenn der Platzhalter angezeigt werden soll, wenn die Textansicht leer ist, auch wenn die Textansicht ausgewählt ist:

Setzen Sie zuerst den Platzhalter in viewDidLoad:

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

textView.becomeFirstResponder()

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

(Hinweis: Da das OP wollte, dass die Textansicht ausgewählt wird, sobald die Ansicht geladen wird, habe ich die Auswahl der Textansicht in den obigen Code aufgenommen. Wenn dies nicht Ihr gewünschtes Verhalten ist und Sie nicht möchten, dass die Textansicht beim Laden der Ansicht ausgewählt wird, Entfernen Sie die letzten beiden Zeilen aus dem obigen Codeblock.)

Verwenden Sie dann die shouldChangeTextInRange UITextViewDelegateMethode wie folgt:

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
}

Und implementieren Sie auch, um textViewDidChangeSelectionzu verhindern, dass der Benutzer die Position des Cursors ändert, während der Platzhalter sichtbar ist. (Hinweis: textViewDidChangeSelectionWird aufgerufen, bevor die Ansicht geladen wird. Überprüfen Sie daher die Farbe der Textansicht nur, 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)
        }
    }
}

5
Hallo, stellen Sie sicher, dass Sie Ihren View Controller als Delegaten für Ihre textView festlegen . Sie können dies erreichen, indem Sie einen Ausgang von Ihrer Textansicht zu Ihrem viewController erstellen. Dann verwenden yourTextField.delegate = self. Wenn Sie dies nicht tun, funktionieren textViewDidBeginEditingdie textViewDidEndEditingFunktionen und nicht.
Ujjwal-Nadhani

2
Der Code wird nicht kompiliert, ich habe einen Fehler als Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter

4
Ich habe die Lösung gefunden und werde den überarbeiteten Code @iPeter anhängen. Der aktuelle Text muss im NSString-Format vorliegen : let currentText = textView.text as NSString?. Transformiere die let updatedText =Linie in let updatedText = currentText?.replacingCharacters(in: range, with: text). Zum Schluss transformieren Sie die if updatedText.isEmptyLinie in if (updatedText?.isEmpty)! {. Das sollte den Trick machen!
Baylor Mitchell

1
@ TàTruhoada Ich habe die Lösung aktualisiert, um Szenarien zum Kopieren und Einfügen zu behandeln
Lyndsey Scott

3
@ LyndseyScott Einstellung textView.selectedTextRangevon innen func textViewDidChangeSelection(_ textView: UITextView)verursacht eine Endlosschleife ...
MikeG

229

Schwimmender Platzhalter


Es ist einfach, sicher und zuverlässig, eine Platzhalterbezeichnung über einer Textansicht zu positionieren, Schriftart und Farbe festzulegen und die Sichtbarkeit von Platzhaltern zu verwalten, indem Änderungen an der Zeichenanzahl der Textansicht verfolgt werden.

Swift 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: Gleiche, ausgenommen italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor



19
Diese Methode ist zweifellos die einfachste und am wenigsten fehleranfällige, die ich gefunden habe. Fabelhaft.
David

6
Es ist eine gute Methode, aber Beschriftungstext kann ungebunden sein, wenn der Platzhaltertext sehr lang ist.
Aks

5
Einfach zu bedienen und gut in das vorhandene Verhalten zu integrieren. Die Platzhalternachricht sollte sowieso nicht so ausführlich sein, aber würde das Setzen der Zeilen auf 0 dieses Problem nicht beheben?
Tommie C.

2
Im Allgemeinen bevorzuge ich diese Methode, obwohl es problematisch ist, wenn der Text mittig ausgerichtet ist, da der Cursor auf dem Platzhalter anstatt links davon zentriert wird.
Blwinters

1
@blwinters - Das wäre ein wirklich ungewöhnlicher Eckfall, nicht wahr? Aber wenn es sich in diesem Fall um ein mehrzeiliges Eingabefeld handelt (das muss ich annehmen), können Sie dann nicht einfach die Berechnung des vertikalen Versatzes anpassen, um den Platzhaltertext ein wenig vom Cursor zu entfernen?
klares Licht

35

Es wird dringend empfohlen, die KMPlaceholderTextView- Bibliothek zu verwenden. Sehr einfach zu bedienen.


1
Dies ist bei weitem die einfachste Lösung (beinhaltet nicht das Hinzufügen von UILabels oder das Ausführen spezieller Delegate-Aufgaben). Funktioniert sofort.
HixField

Es sollte funktionieren. Der Autor sagte, dass es Swift 3.0+ unterstützt
t4nhpt

27

Schnell:

Fügen Sie Ihre Textansicht programmgesteuert oder über den Interface Builder hinzu. Wenn dies der letzte ist, erstellen Sie den Ausgang:

@IBOutlet weak var yourTextView: UITextView!

Bitte fügen Sie den Delegaten (UITextViewDelegate) hinzu:

class ViewController: UIViewController, UITextViewDelegate {

Fügen Sie in der viewDidLoad-Methode Folgendes hinzu:

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

Lassen Sie mich nun den magischen Teil vorstellen und diese Funktion hinzufügen:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Beachten Sie, dass dies immer dann ausgeführt wird, wenn die Bearbeitung beginnt. Dort überprüfen wir die Bedingungen, um den Status mithilfe der Farbeigenschaft zu ermitteln. Das Setzen von Text auf nilIch empfehle nicht. Gleich danach setzen wir die Textfarbe auf die gewünschte, in diesem Fall Schwarz.

Fügen Sie nun auch diese Funktion hinzu:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Lassen Sie mich darauf bestehen, nicht zu vergleichen nil, ich habe das bereits versucht und es würde nicht funktionieren. Anschließend setzen wir die Werte wieder auf den Platzhalterstil und die Farbe wieder auf die Platzhalterfarbe, da dies eine Bedingung zum Einchecken ist textViewDidBeginEditing.


16

Verwenden Sie diese Erweiterung. Dies ist der beste Weg, um Platzhalter in UITextView festzulegen. Stellen Sie jedoch sicher, dass Sie Delegierte an die Textansicht angehängt haben. Sie können den Platzhalter wie folgt einstellen: -

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
Arbeitete wie Zauber !! Vielen Dank ! :)
Nahid Raihan

Willkommen Liebes Alles Gute
Sandip Gill

15

Ich bin überrascht, dass niemand erwähnt hat NSTextStorageDelegate. UITextViewDelegateDie Methoden werden nur durch Benutzerinteraktion ausgelöst, nicht jedoch programmgesteuert. Wenn Sie beispielsweise die textEigenschaft einer Textansicht programmgesteuert festlegen, müssen Sie die Sichtbarkeit des Platzhalters selbst festlegen, da die Delegatmethoden nicht aufgerufen werden.

Doch mit NSTextStorageDelegate‚s - textStorage(_:didProcessEditing:range:changeInLength:)Methode, werden Sie auf den Text über jede Änderung informiert werden, auch wenn es programmatisch fertig ist. Weisen Sie es einfach so zu:

textView.textStorage.delegate = self

(In UITextViewist diese Delegate-Eigenschaft nilstandardmäßig, sodass sie kein Standardverhalten beeinflusst.)

Kombinieren Sie es mit der UILabelTechnik @clearlight zeigt, kann man leicht die ganze wickeln UITextView‚s placeholderImplementierung in eine Verlängerung.

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, dass die Verwendung einer privaten (verschachtelten) Klasse aufgerufen wird PlaceholderLabel. Es gibt überhaupt keine Implementierung, aber es bietet uns eine Möglichkeit, die Platzhalterbezeichnung zu identifizieren, die weitaus schneller ist als die Verwendung der tagEigenschaft.

Mit diesem Ansatz können Sie den Delegaten von weiterhin UITextVieweiner anderen Person zuweisen .

Sie müssen nicht einmal die Klassen Ihrer Textansichten ändern. Fügen Sie einfach die Erweiterung (en) hinzu, und Sie können jedem UITextViewin Ihrem Projekt eine Platzhalterzeichenfolge zuweisen , auch im Interface Builder.

Ich habe die Implementierung einer placeholderColorEigenschaft aus Gründen der Klarheit weggelassen, aber sie kann nur für ein paar weitere Zeilen mit einer ähnlichen berechneten Variablen wie implementiert werden placeholder.


Sehr elegante Lösung. Ich liebe es.
Dave Batton

textView.textStorage.delegate = selfDies erfordert in einem View-Controller, dass wir diesen View-Controller mit binden NSTextStorageDelegate. Es ist wirklich erforderlich?
Hemang

Einfach und wirkt wie ein Zauber. Diese Antwort muss akzeptiert werden
Qaiser Abbas

@Hemang es ist die Textansicht selbst NSTextStorageDelegate, nicht der View Controller.
Yesleon

14

Ich habe dazu zwei verschiedene Textansichten verwendet:

  1. Eine im Hintergrund, die als Platzhalter verwendet wird.
  2. Eine im Vordergrund (mit transparentem Hintergrund), die der Benutzer tatsächlich eingibt.

Die Idee ist, dass der Platzhalter im Hintergrund verschwindet (und wieder angezeigt wird, wenn der Benutzer alles löscht), sobald der Benutzer mit der Eingabe von Inhalten in der Vordergrundansicht beginnt. Es verhält sich also genau wie ein Platzhalter für das einzeilige Textfeld.

Hier ist der Code, den ich dafür verwendet habe. Beachten Sie, dass descriptionField das Feld ist, in das der Benutzer eingibt, und descriptionPlaceholder das Feld im Hintergrund ist.

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

1
Dieser Weg ist ein bisschen hackig, aber bei weitem der einfachste und generiert genau die gewünschten Ergebnisse. Gute Idee
William T.

9

Aufgrund einiger der großartigen Vorschläge hier konnte ich die folgende leichte, Interface-Builder-kompatible Unterklasse zusammenstellen, von UITextViewder:

  • Enthält konfigurierbaren Platzhaltertext, der genau wie der von gestaltet ist UITextField.
  • Benötigt keine zusätzlichen Unteransichten oder Einschränkungen.
  • Erfordert keine Delegierung oder anderes Verhalten vom ViewController.
  • Benötigt keine Benachrichtigungen.
  • Hält diesen Text vollständig von externen Klassen getrennt, die die textFeldeigenschaft betrachten.

Verbesserungsvorschläge sind willkommen, insbesondere wenn es eine Möglichkeit gibt, die Platzhalterfarbe von iOS programmgesteuert abzurufen, anstatt sie fest zu codieren.

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

SET-Wert beim Laden der Ansicht

    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

Ich habe versucht, den Code anhand der Antwort von clearlight zu vereinfachen .

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
    }

}

Verwendungszweck

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

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

Ein paar Probleme. (1) Als Erweiterung, die einen UIView-Tag-Eigenschaftswert annimmt und "ausleiht", besteht das Risiko, dass jemand dasselbe Tag in seiner eigenen Ansichtshierarchie verwendet, ohne die Verwendung der Erweiterung zu kennen, was zu einem äußerst schwer zu diagnostizierenden Fehler führt. Solche Dinge gehören nicht in Bibliothekscode oder Erweiterungen. (2) Der Anrufer muss weiterhin einen Delegierten deklarieren. Der Floating Placeholder vermeidet Hacks, hat einen winzigen Platzbedarf, ist einfach und vollständig lokal, was ihn zu einer sicheren Wette macht.
klares Licht

5

Eine weitere Lösung (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)
    }
}

Ergebnis

Geben Sie hier die Bildbeschreibung ein


4

Eine einfache und schnelle Lösung, die für mich funktioniert, ist:

@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

Hier ist, was ich für diesen Job benutze.

@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()
        }
    }

}

Ich weiß, dass es mehrere ähnliche Ansätze gibt, aber die Vorteile dieses Ansatzes sind:

  • Kann Platzhaltertext, Schriftgröße und Farbe in IB festlegen .
  • Die Warnung " Bildlaufansicht hat mehrdeutigen Bildlaufinhalt " wird in IB nicht mehr angezeigt .
  • Fügen Sie eine Animation zum Ein- / Ausblenden des Platzhalters hinzu.

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

Wenn der Benutzer mit der Bearbeitung des Aufrufs textViewDidBeginEditing beginnt und dann überprüft, ob die Farbe von Textgrau bedeutet, dass der Benutzer nichts geschrieben hat, setzen Sie die Textansicht auf Null und ändern Sie die Farbe für die SMS des Benutzers in Schwarz.

Wenn der Benutzer die Bearbeitung von textViewDidEndEditing beendet und aufruft und prüft, ob der Benutzer nichts in die Textansicht schreibt, wird der Text als graue Farbe mit dem Text "PlaceHolder" festgelegt.


3

Hier ist meine Art, dieses Problem zu lösen ( Swift 4 ):

Die Idee war, eine möglichst einfache Lösung zu finden, die es ermöglicht, Platzhalter in verschiedenen Farben zu verwenden, die Größe auf Platzhaltergröße zu ändern und nicht zu überschreiben, während delegatealle UITextViewFunktionen wie erwartet funktionieren.

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

Dies ist eine großartige und einfache Lösung.
JaredH

2

Ich weiß nicht, warum die Leute dieses Problem so sehr komplizieren ... Es ist ziemlich einfach und unkompliziert. Hier ist eine Unterklasse von UITextView, die die angeforderte Funktionalität bereitstellt.

- (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];
    }`

Zu Ihrer Information, bevor mich jemand schlägt ... das ist ziemlich direkt, um es in Swift zu übersetzen. Hier ist nichts kompliziert.
TheCodingArt

2

Dies ist meine gebrauchsfertige Lösung, wenn Sie mit mehreren Textansichten arbeiten

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

Es ist sehr schön!
KevinVuD

2

Swift 3.1

Diese Erweiterung hat bei mir gut funktioniert: https://github.com/devxoul/UITextView-Placeholder

Hier ist ein Code-Snippet:

Installieren Sie es über Pod:

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

Importieren Sie es in Ihre Klasse

import UITextView_Placeholder

Und fügen Sie placeholderIhrem bereits erstellten Objekt eine Eigenschaft hinzuUITextView

textView.placeholder = "Put some detail"

Das ist es ... Hier, wie es aussieht (dritte Box ist a UITextView) Geben Sie hier die Bildbeschreibung ein


2

Im Gegensatz zu fast jeder Antwort auf dieses Thema liegt , UITextView hat einen Platzhalter Eigenschaft. Aus Gründen, die ich nicht verstehe, wird es nur in IB als solches angezeigt:

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

Wenn Sie also Storyboards verwenden und ein statischer Platzhalter ausreicht, legen Sie die Eigenschaft einfach im Inspektor fest.

Sie können diese Eigenschaft auch im folgenden Code festlegen:

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

Seine trübe , da dies Wetter über private API zugegriffen wird , wie die Eigenschaft wird ausgesetzt.

Ich habe nicht versucht, mit dieser Methode einzureichen. Aber ich werde diesen Weg in Kürze einreichen und diese Antwort entsprechend aktualisieren.

AKTUALISIEREN:

Ich habe diesen Code in mehreren Versionen ohne Probleme von Apple ausgeliefert.

UPDATE: Dies funktioniert nur in Xcode vor 11.2


In Swift 5 können Sie myTextView schreiben, placeholder = "Geben Sie Ihre Augenfarbe ein"
user462990

@ user462990 Können Sie hierfür einen Dokumentationslink bereitstellen? Ich glaube nicht, dass das richtig ist.
Alex Chase

Entschuldigung, habe dox nicht gefunden, aber es ist wirklich einfach zu testen ... zB "alert.addTextField {(textField3) in textField3.placeholder = language.JBLocalize (Ausdruck:" yourName ")}. jbLocalize ist ein Übersetzungsmanager, der a zurückgibt String
user462990

3
@ user462990 Sie verweisen UITextFieldNICHT UITextView Bitte lesen Sie die Fragen / Antworten genauer durch.
Alex Chase

3
Tolle Lösung, funktioniert aber bei mir nicht. Irgendwie stürzt es immer ab. Können Sie die von Ihnen verwendeten Bereitstellungsziel-, Swift- und xCode-Versionen angeben?
T. Pasichnyk

1

In ios gibt es keine solche Eigenschaft, um Platzhalter direkt in TextView hinzuzufügen. Sie können stattdessen eine Beschriftung hinzufügen und die Änderung in textView ein- / ausblenden. SWIFT 2.0 und stellen Sie sicher, dass Sie das textviewdelegate implementieren

func textViewDidChange(TextView: UITextView)
{

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

}

1

Swift - Ich habe eine Klasse geschrieben, die UITextView geerbt hat, und ein UILabel als Unteransicht hinzugefügt, um als Platzhalter zu fungieren.

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

}

Ein gutes Beispiel für die Verwendung von Einschränkungen zum Positionieren des Platzhalters. Die ideale Position des Platzhalters befindet sich jedoch genau über der Stelle, an der das erste Zeichen des Textes platziert werden soll. Daher ist es besser, diese Position anhand der Schriftart der Textansicht zu berechnen, da sie sich anpasst auf welche Schriftgröße auch immer für die Textansicht eingestellt ist.
Clearlight

1

Ich mag die Lösung von @ nerdist. Auf dieser Grundlage habe ich eine Erweiterung erstellt für 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
    }
  }
}

In einer ViewController-Klasse verwende ich sie beispielsweise folgendermaßen:

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

Platzhalter auf UITextview!

Geben Sie hier die Bildbeschreibung ein

UPDATE :

Wenn Sie den Text der Textansicht im Code ändern, denken Sie daran, die updateVisibitly-Methode aufzurufen, um Platzhalter auszublenden:

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

Um zu verhindern, dass der Platzhalter mehrmals hinzugefügt wird, wird eine private add()Funktion hinzugefügt extension.


Nochmals vielen Dank für Ihre Verbesserung gegenüber dem Original. Ich habe an diesem Wochenende viel zu viel Zeit damit verbracht, aber ich denke, Sie werden die extreme Variante von EZ Placeholder genießen, die ich mir schließlich ausgedacht habe : stackoverflow.com/a/41081244/2079103 (Ich habe Ihnen die Ehre
Clearlight

1

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

}}

In swift3:

import UIKit

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

}}

Ich habe schnell eine Klasse geschrieben. Sie müssen diese Klasse bei Bedarf importieren.


Bitte erwähnen Sie, für welche Version von Swift dies ist (funktioniert nicht für Swift 3)
Clearlight

1

Ich kann wegen des guten Rufs keinen Kommentar hinzufügen. Fügen Sie in @clearlight answer einen weiteren Delegiertenbedarf hinzu.

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

ist notwendig

weil textViewDidChangewird nicht beim ersten mal aufgerufen


1

Nein, für die Textansicht ist kein Platzhalter verfügbar. Sie müssen eine Beschriftung darüber setzen, wenn der Benutzer in die Textansicht eingibt, diese dann ausblenden oder als Standardwert festlegen, wenn der Benutzer alle Werte entfernt.


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

Ich musste die Warteschlange absenden, damit mein Platzhaltertext nach Abschluss der Bearbeitung wieder angezeigt wurde.

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

Was gebe ich anstelle der letzten Zeile "textView.text =" Description "ein, wenn dieser Wert dem entsprechen soll, den der Benutzer eingibt?
ISS

1

Schnell:

Fügen Sie hinzu TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

Fügen Sie in der viewWillAppearMethode Folgendes hinzu:

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

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

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

}

Bitte fügen Sie die DelegateUsing-Erweiterung (UITextViewDelegate) hinzu:

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

Unsere Lösung vermeidet das Durcheinander mit den Eigenschaften UITextView textund textColor, was praktisch ist, wenn Sie einen Zeichenzähler pflegen.

Es ist einfach:

1) Erstellen Sie UITextViewim Storyboard einen Dummy mit denselben Eigenschaften wie der Master UITextView. Weisen Sie dem Dummy-Text Platzhaltertext zu.

2) Richten Sie den oberen, linken und rechten Rand der beiden aus UITextViews.

3) Platzieren Sie den Dummy hinter dem Master.

4) Überschreiben Sie die textViewDidChange(textView:)Delegatenfunktion des Masters und zeigen Sie den Dummy an, wenn der Master 0 Zeichen hat. Andernfalls zeigen Sie den Master.

Dies setzt voraus, dass beide UITextViewstransparente Hintergründe haben. Wenn dies nicht der Fall ist, platzieren Sie den Dummy oben, wenn 0 Zeichen vorhanden sind, und schieben Sie ihn darunter, wenn> 0 Zeichen vorhanden sind. Sie müssen auch die Responder austauschen, um sicherzustellen, dass der Cursor nach rechts folgt UITextView.


1

Swift 4, 4.2 und 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]

es funktionierte ! Danke
Rakshitha Muranga Rodrigo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.