Come posso aggiungere testo segnaposto all'interno di un UITextView in Swift?


316

Sto facendo una domanda che utilizza un UITextView. Ora voglio che la vista di testo abbia un segnaposto simile a quello che puoi impostare per un campo di testo. Come realizzeresti questo usando Swift?


Questo è un problema secolare nello sviluppo di iOS con UITextView. Ho scritto sottoclassi come quella menzionata qui: stackoverflow.com/a/1704469/1403046 . Il vantaggio è che è ancora possibile avere un delegato, nonché utilizzare la classe in più posizioni senza dover implementare nuovamente la logica.
cjwirth,

Come utilizzerei la tua sottoclasse, usando swift per il progetto. Usi un file bridge?
StevenR,

Potresti farlo o implementarlo nuovamente in Swift. Il codice nella risposta è più lungo di quanto non debba essere. Il punto principale è mostrare / nascondere l'etichetta aggiunta nel metodo per il quale si riceve una notifica quando il testo cambia.
cjwirth,

Puoi usare l'esempio UIFloatLabelTextView da GitHub. Questa posizione segnaposto in alto durante la scrittura. Davvero interessante! github.com/ArtSabintsev/UIFloatLabelTextView
Jayprakash Dubey

2
Onestamente, il modo più semplice per ottenere questo risultato è avere un textView personalizzato e aggiungere semplicemente il testo segnaposto che viene disegnato sul textView quando non è presente alcun testo .... Ogni altra risposta finora è stata una versione molto complicata di questo che comporta problemi gestione dello stato (compresi i falsi positivi per quando il testo dovrebbe / non dovrebbe / non esiste)
TheCodingArt

Risposte:


649

Aggiornato per Swift 4

UITextViewnon ha intrinsecamente una proprietà segnaposto, quindi dovresti crearne e manipolarne una a livello di codice usando i UITextViewDelegatemetodi. Consiglio di utilizzare la soluzione 1 o 2 di seguito in base al comportamento desiderato.

Nota: per entrambe le soluzioni, aggiungere UITextViewDelegatealla classe e impostare textView.delegate = selfl'utilizzo dei metodi delegati della vista di testo.


Soluzione n. 1 : se si desidera che il segnaposto scompaia non appena l'utente seleziona la vista di testo:

Innanzitutto impostare il UITextViewper contenere il testo segnaposto e impostarlo su un colore grigio chiaro per imitare l'aspetto del UITextFieldtesto segnaposto di un. O farlo nella viewDidLoadcreazione della vista di testo o su di essa.

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

Quindi quando l'utente inizia a modificare la vista del testo, se la vista del testo contiene un segnaposto (ovvero se il suo colore del testo è grigio chiaro) cancella il testo del segnaposto e imposta il colore del testo su nero per adattarlo alla voce dell'utente.

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

Quindi, quando l'utente termina la modifica della vista di testo e viene rassegnato come primo risponditore, se la vista di testo è vuota, reimpostare il segnaposto aggiungendo nuovamente il testo del segnaposto e impostando il suo colore su grigio chiaro.

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

Soluzione n. 2 : se si desidera che il segnaposto venga visualizzato ogni volta che la vista di testo è vuota, anche se la vista di testo è selezionata:

Innanzitutto imposta il segnaposto in viewDidLoad:

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

textView.becomeFirstResponder()

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

(Nota: poiché l'OP voleva che la vista di testo fosse selezionata non appena la vista veniva caricata, ho incorporato la selezione della vista di testo nel codice sopra. Se questo non è il comportamento desiderato e non vuoi che la vista di testo sia selezionata al caricamento della vista, rimuovere le ultime due righe dal blocco di codice sopra.)

Quindi utilizzare il shouldChangeTextInRange UITextViewDelegatemetodo, in questo modo:

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
}

E implementare anche textViewDidChangeSelectionper impedire all'utente di modificare la posizione del cursore mentre il segnaposto è visibile. (Nota: textViewDidChangeSelectionviene chiamato prima del caricamento della vista, quindi controlla il colore della vista di testo solo se la finestra è visibile):

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
Salve, assicurati di impostare il controller della vista come delegato per textView . Puoi farlo creando uno sbocco dal tuo textView al tuo viewController. Quindi utilizzare yourTextField.delegate = self. In caso contrario, textViewDidBeginEditingle textViewDidEndEditingfunzioni e non funzioneranno.
Ujjwal-Nadhani,

2
Il codice non si sta compilando, sto riscontrando un errore come Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter,

4
Ho trovato la soluzione e allego il codice rivisto @iPeter. Il testo attuale deve essere in NSString formanti: let currentText = textView.text as NSString?. Trasforma la let updatedText =linea in let updatedText = currentText?.replacingCharacters(in: range, with: text). Infine, trasforma la if updatedText.isEmptylinea in if (updatedText?.isEmpty)! {. Questo dovrebbe fare il trucco!
Baylor Mitchell,

1
@ TàTruhoada Ho aggiornato la soluzione per gestire gli scenari di copia e incolla
Lyndsey Scott

3
L'impostazione di @LyndseyScott textView.selectedTextRangedall'interno func textViewDidChangeSelection(_ textView: UITextView)provoca un ciclo infinito ...
MikeG

229

Segnaposto galleggiante


È semplice, sicuro e affidabile posizionare un'etichetta segnaposto sopra una vista di testo, impostarne il carattere, il colore e gestire la visibilità del segnaposto monitorando le modifiche al conteggio dei caratteri della vista di testo.

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: uguali, tranne: italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor



19
Questo metodo è innegabilmente il più semplice e meno soggetto a errori che abbia mai trovato. Favoloso.
David,

6
È un buon metodo ma il testo dell'etichetta potrebbe andare fuori limite se il testo del segnaposto è molto lungo ..
Chiede il

5
Semplice da usare e si integra perfettamente con il comportamento esistente. Il messaggio segnaposto non dovrebbe essere comunque così dettagliato, tuttavia impostare le righe su 0 non si occuperebbe di questo problema?
Tommie C.,

2
In genere preferisco questo metodo, sebbene sia problematico se il testo è allineato al centro perché il cursore sarà centrato sulla parte superiore del segnaposto anziché a sinistra di esso.
Blwinters

1
@blwinters - Sarebbe un caso davvero insolito? Ma supponendo che sia un campo di input su più righe in quel caso (devo ammetterlo), non potresti semplicemente regolare il calcolo dell'offset verticale per spostare un po 'il testo del segnaposto dal cursore?
Clearlight,

35

Consiglio vivamente di usare la libreria KMPlaceholderTextView . Molto semplice da usare


1
Questa è di gran lunga la soluzione più semplice (non comporta l'aggiunta di UILabels o l'esecuzione di speciali operazioni delegate). Funziona fuori dalla scatola.
HixField,

Dovrebbe funzionare. L'autore ha affermato che supporta Swift 3.0+
t4nhpt,

27

Swift:

Aggiungi la tua visualizzazione di testo a livello di codice o tramite Interface Builder, se l'ultimo, crea il punto vendita:

@IBOutlet weak var yourTextView: UITextView!

Aggiungi il delegato (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

Nel metodo viewDidLoad, aggiungi quanto segue:

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

Ora lascia che ti presenti la parte magica, aggiungi questa funzione:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Nota che questo verrà eseguito ogni volta che inizia la modifica, lì controlleremo le condizioni per dire lo stato, usando la proprietà color. nilNon è consigliabile impostare il testo su . Subito dopo, impostiamo il colore del testo su desiderato, in questo caso il nero.

Ora aggiungi anche questa funzione:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Permettetemi di insistere, non fare confronti nil, l'ho già provato e non funzionerebbe. Quindi riportiamo i valori allo stile segnaposto e riportiamo il colore al colore segnaposto perché è una condizione per il check-in textViewDidBeginEditing.


16

Usa questa estensione questo è il modo migliore per impostare il segnaposto in UITextView. Ma assicurati di aver collegato delegati a TextView. Puoi impostare il segnaposto in questo modo: -

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
Ha funzionato come ciondoli !! Grazie mille! :)
Nahid Raihan,

Benvenuto Caro tutto il meglio
Sandip Gill,

15

Sono sorpreso che nessuno abbia menzionato NSTextStorageDelegate. UITextViewDelegateI metodi saranno attivati ​​solo dall'interazione dell'utente, ma non a livello di programmazione. Ad esempio, quando imposti la textproprietà di una vista di testo a livello di codice , dovrai impostare tu stesso la visibilità del segnaposto, poiché i metodi delegati non verranno chiamati.

Tuttavia, con NSTextStorageDelegateil textStorage(_:didProcessEditing:range:changeInLength:)metodo di, sarai avvisato di qualsiasi modifica al testo, anche se è stata programmata. Assegnalo in questo modo:

textView.textStorage.delegate = self

(In UITextView, questa proprietà delegata è nilper impostazione predefinita, quindi non influirà su alcun comportamento predefinito.)

Combinate con la UILabeltecnica @clearlight dimostra, si può avvolgere facilmente tutta la UITextView's placeholderimplementazione in un prolungamento.

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

}

Si noti che viene chiamato l'uso di una classe privata (nidificata) PlaceholderLabel. Non ha alcuna implementazione, ma ci fornisce un modo per identificare l'etichetta segnaposto, che è molto più "veloce" rispetto all'uso della tagproprietà.

Con questo approccio, è ancora possibile assegnare il delegato di UITextViewa qualcun altro.

Non è nemmeno necessario modificare le classi delle visualizzazioni di testo. Aggiungi solo l'estensione (e) e sarai in grado di assegnare una stringa segnaposto a ciascuna UITextViewnel tuo progetto, anche in Interface Builder.

Ho omesso l'implementazione di una placeholderColorproprietà per motivi di chiarezza, ma può essere implementata solo per poche altre righe con una variabile calcolata simile a placeholder.


Soluzione molto elegante. Lo adoro.
Dave Batton,

textView.textStorage.delegate = selfquesto in un controller di visualizzazione richiederà di associare quel controller di visualizzazione con NSTextStorageDelegate. È davvero necessario?
Hemang,

Semplice e funziona come un fascino. Questa deve essere una risposta accettata
Qaiser Abbas,

@Hemang è la vista di testo stessa NSTextStorageDelegate, non il controller di vista.
Sì,

14

L'ho fatto usando due diverse visualizzazioni di testo:

  1. Uno in background utilizzato come segnaposto.
  2. Uno in primo piano (con uno sfondo trasparente) in cui l'utente digita effettivamente.

L'idea è che una volta che l'utente inizia a digitare elementi nella vista in primo piano, il segnaposto in background scompare (e riappare se l'utente elimina tutto). Quindi si comporta esattamente come un segnaposto per il campo di testo a riga singola.

Ecco il codice che ho usato per questo. Si noti che descriptionField è il campo in cui l'utente digita e descriptionPlaceholder è quello in background.

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

1
In questo modo è un po 'confuso, ma è di gran lunga il più semplice e genera esattamente i risultati desiderati. Buona idea
William T.

9

Sulla base di alcuni dei fantastici suggerimenti qui, sono stato in grado di mettere insieme la seguente sottoclasse leggera, compatibile con Interface-Builder UITextView, che:

  • Include testo segnaposto configurabile, in stile proprio come quello di UITextField.
  • Non richiede ulteriori visualizzazioni secondarie o vincoli.
  • Non richiede alcuna delega o altro comportamento da ViewController.
  • Non richiede alcuna notifica.
  • Mantiene il testo completamente separato da qualsiasi classe esterna guardando la textproprietà del campo .

Eventuali suggerimenti di miglioramento sono ben accetti, soprattutto se esiste un modo per estrarre programmaticamente il colore segnaposto di iOS, piuttosto che codificarlo.

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

Valore SET nel caricamento vista

    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

Ho cercato di rendere il codice conveniente dal clearlight 's risposta .

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
    }

}

uso

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

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

Un paio di problemi. (1) Come estensione che assume e "prende in prestito" un valore della proprietà del tag UIView, esiste il rischio che qualcuno possa utilizzare lo stesso tag nella propria gerarchia di visualizzazioni, ignaro dell'utilizzo dell'estensione, creando un bug estremamente difficile da diagnosticare. Cose del genere non appartengono al codice della libreria o alle estensioni. (2) Richiede comunque al chiamante di dichiarare un delegato. Il segnaposto mobile evita gli hack, ha un ingombro ridotto, è semplice e interamente locale, il che lo rende una scommessa sicura.
chiarimento il

5

Un'altra soluzione (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)
    }
}

risultato

inserisci qui la descrizione dell'immagine


4

Una soluzione semplice e veloce che funziona per me è:

@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

Ecco cosa sto usando per questo lavoro svolto.

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

}

So che ci sono diversi approcci simili a questo, ma i vantaggi di questo sono:

  • È possibile impostare il testo segnaposto, la dimensione del carattere e il colore in IB .
  • Non mostra più l'avvertenza di " Scroll View ha contenuti scorrevoli ambigui " in IB.
  • Aggiungi animazione per mostrare / nascondere il segnaposto.

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

Innanzitutto quando l'utente inizia a modificare textViewDidBeginEditing call e quindi controlla se il colore del testo in grigio indica che l'utente non ha scritto nulla, quindi imposta come textview zero e cambia il colore in nero per inviare messaggi all'utente.

Quando l'utente termina la modifica di textViewDidEndEditing è una chiamata e controlla se l'utente non scrive nulla nella visualizzazione di testo, il testo viene impostato come colore grigio con il testo "PlaceHolder"


3

Ecco il mio modo di risolvere questo problema ( Swift 4 ):

L'idea era quella di creare la soluzione più semplice possibile che consenta di utilizzare segnaposto di diversi colori, ridimensionare in base alle dimensioni dei segnaposto, senza sovrascrivere delegatenel frattempo mantenendo tutte le UITextViewfunzioni come previsto.

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

Questa è una soluzione grande e semplice.
JaredH

2

Non so perché le persone complicino così tanto questo problema ... È abbastanza semplice e diretto. Ecco una sottoclasse di UITextView che fornisce la funzionalità richiesta.

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

Cordiali saluti, prima che qualcuno mi picchi ad esso ... questo è abbastanza diretto da tradurre in Swift. Niente di complicato qui.
TheCodingArt

2

Questa è la mia soluzione pronta per l'uso se lavori con più visualizzazioni di testo

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

È molto carino!
KevinVuD,

2

Swift 3.1

Questa estensione ha funzionato bene per me: https://github.com/devxoul/UITextView-Placeholder

Ecco uno snippet di codice:

Installalo tramite pod:

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

Importalo nella tua classe

import UITextView_Placeholder

E aggiungi placeholderproprietà al tuo già creatoUITextView

textView.placeholder = "Put some detail"

Ecco ... Ecco come appare (la terza casella è a UITextView) inserisci qui la descrizione dell'immagine


2

Contrariamente a quasi ogni risposta su questo post, UITextView ha una proprietà segnaposto. Per ragioni al di là della mia comprensione, è esposto solo in IB, in quanto tale:

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

Quindi, se si utilizzano storyboard e sarà sufficiente un segnaposto statico, impostare la proprietà sull'ispettore.

Puoi anche impostare questa proprietà in un codice come questo:

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

È nuvoloso per quanto riguarda il tempo a cui si accede tramite API privata, poiché la proprietà è esposta.

Non ho provato a inviare con questo metodo. Ma presenterò in questo modo a breve e aggiornerò di conseguenza questa risposta.

AGGIORNARE:

Ho spedito questo codice in più versioni senza problemi da parte di Apple.

AGGIORNAMENTO: Funzionerà solo con Xcode pre 11.2


in Swift 5 puoi scrivere myTextView, placeholder = "inserisci il colore degli occhi"
user462990

@ user462990 Puoi fornire un collegamento alla documentazione per questo? Non credo sia accurato.
Alex Chase,

scusa, non ho trovato dox ma è davvero facile testarlo ... ad es. "alert.addTextField {(textField3) in textField3.placeholder = language.JBLocalize (frase:" yourName ")}. jbLocalize è un gestore delle traduzioni che restituisce un String
user462990

4
@ user462990 Ti stai riferendo a UITextFieldNON UITextView leggere attentamente le domande / risposte.
Alex Chase,

3
Ottima soluzione, ma non funziona per me. In qualche modo si blocca sempre. Potresti specificare le versioni target di destinazione, swift e xCode che stai utilizzando?
T. Pasichnyk,

1

Non esiste una proprietà del genere in iOS per aggiungere segnaposto direttamente in TextView piuttosto è possibile aggiungere un'etichetta e mostrare / nascondere sulla modifica in textView. SWIFT 2.0 e assicurati di implementare il delegato del visualizzatore di testo

func textViewDidChange(TextView: UITextView)
{

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

}

1

Swift: ho scritto una classe che ha ereditato UITextView e ho aggiunto una UILabel come sottoview per agire come segnaposto.

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

}

Grande esempio dell'uso dei vincoli per posizionare il segnaposto, tuttavia la posizione ideale del segnaposto è proprio sopra dove andrà il primo carattere del testo, quindi calcolare quella posizione in base al carattere della vista di testo è meglio per questo perché si adatta a qualsiasi dimensione del carattere sia impostata per la visualizzazione del testo.
Clearlight il

1

Mi piace la soluzione di @ nerdist. Sulla base di ciò, ho creato un'estensione per 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 una classe ViewController, ad esempio, è così che lo uso:

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

Segnaposto su UITextview!

inserisci qui la descrizione dell'immagine

AGGIORNAMENTO :

Nel caso in cui modifichi il testo di textview nel codice, ricorda di chiamare il metodo updateVisibitly per nascondere il segnaposto:

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

Per impedire che il segnaposto venga aggiunto più di una volta, add()viene aggiunta una funzione privata extension.


Grazie ancora per il tuo miglioramento all'originale. Ho trascorso molto tempo in questo fine settimana, ma penso che ti piacerà la variante estrema di EZ Placeholder che alla fine mi è venuta in mente: stackoverflow.com/a/41081244/2079103 (ti ho dato credito per aver contribuito all'evoluzione del soluzioni segnaposto)
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

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

}

Ho scritto un corso in rapido. È necessario importare questa classe quando richiesto.



1

Non posso aggiungere commenti a causa della reputazione. aggiungere un'altra necessità delegato nella risposta @clearlight.

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

è necessario

perché textViewDidChangenon si chiama prima volta


1

no, non è disponibile alcun segnaposto per la visualizzazione di testo. devi inserire un'etichetta sopra di esso quando l'utente immette in visualizzazione testo quindi nasconderlo o impostarlo per impostazione predefinita quando l'utente immette rimuovere tutti i valori.


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

Ho dovuto inviare la coda per far riapparire il testo del mio segnaposto una volta completata la modifica.

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

Cosa devo digitare invece dell'ultima riga "textView.text =" Descrizione "se voglio che questo valore sia quello che l'utente sta scrivendo?
ISS,

1

Swift:

Aggiungi il tuo TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

Nel viewWillAppearmetodo, aggiungi quanto segue:

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

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

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

}

Aggiungi l' Delegateestensione 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

La nostra soluzione evita la confusione con le proprietà UITextView texte textColor, che è utile se stai mantenendo un contatore di personaggi.

È semplice:

1) Crea un manichino UITextViewnello Storyboard con le stesse proprietà del master UITextView. Assegna il testo segnaposto al testo fittizio.

2) Allinea i bordi superiore, sinistro e destro dei due UITextViews.

3) Posizionare il manichino dietro al maestro.

4) Sostituire la textViewDidChange(textView:)funzione delegata del master e mostrare il manichino se il master ha 0 caratteri. Altrimenti, mostra al maestro.

Ciò presuppone che entrambi UITextViewsabbiano sfondi trasparenti. In caso contrario, posiziona il manichino in cima quando ci sono 0 caratteri e spingilo sotto quando ci sono> 0 caratteri. Dovrai anche scambiare i rispondenti per assicurarti che il cursore segua la destra UITextView.


1

Swift 4, 4.2 e 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]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.