Wie kann ich einen Platzhalter in einem UITextView
hinzufügen, ähnlich wie bei UITextField
, in Swift
?
Antworten
Zu viele Anzeigen?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)
}
}
}
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
}
}
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.
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.
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
}
}
- See previous answers
- Weitere Antworten anzeigen