¿Cómo puedo agregar texto de marcador de posición dentro de un UITextView en Swift?


316

Estoy haciendo una aplicación que usa a UITextView. Ahora quiero que la Vista de texto tenga un marcador de posición similar al que puede establecer para un Campo de texto. ¿Cómo lograrías esto usando Swift?


Este es un problema antiguo en el desarrollo de iOS con UITextView. He escrito subclases como la que se menciona aquí: stackoverflow.com/a/1704469/1403046 . El beneficio es que aún puede tener un delegado, así como usar la clase en varios lugares sin tener que volver a implementar la lógica.
cjwirth

¿Cómo usaría su subclase mientras uso swift para el proyecto? ¿Usando un archivo bridge?
StevenR

Podrías hacer eso o volver a implementarlo en Swift. El código en la respuesta es más largo de lo que realmente tiene que ser. El punto principal es mostrar / ocultar la etiqueta que agregas en el método que se te notifica cuando el texto cambia.
cjwirth

Puede usar la muestra UIFloatLabelTextView de GitHub. Coloque el marcador de posición en la parte superior mientras escribe. Realmente interesante! github.com/ArtSabintsev/UIFloatLabelTextView
Jayprakash Dubey

2
Honestamente, la forma más fácil de lograr esto es tener una vista de texto personalizada y simplemente agregar texto de marcador de posición que se dibuja en la vista de texto cuando no hay texto ... Alguna otra respuesta hasta ahora ha sido una versión muy complicada de esto que involucra problemas gestión del estado (incluyendo falsos positivos para cuándo el texto debería / no debería existir / no existe)
TheCodingArt

Respuestas:


649

Actualizado para Swift 4

UITextViewno tiene inherentemente una propiedad de marcador de posición, por lo que tendría que crear y manipular una mediante programación mediante UITextViewDelegatemétodos. Recomiendo usar cualquiera de las soluciones # 1 o # 2 a continuación dependiendo del comportamiento deseado.

Nota: Para cualquier solución, agregue UITextViewDelegatea la clase y configure textView.delegate = selfpara usar los métodos delegados de la vista de texto.


Solución n. ° 1 : si desea que el marcador de posición desaparezca tan pronto como el usuario seleccione la vista de texto:

Primero configure el UITextViewpara contener el texto del marcador de posición y configúrelo en un color gris claro para imitar el aspecto del UITextFieldtexto del marcador de posición de un. Puede hacerlo en viewDidLoado sobre la creación de la vista de texto.

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

Luego, cuando el usuario comience a editar la vista de texto, si la vista de texto contiene un marcador de posición (es decir, si su color de texto es gris claro), borre el texto del marcador de posición y configure el color del texto en negro para acomodar la entrada del usuario.

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

Luego, cuando el usuario termina de editar la vista de texto y se resigna como el primer respondedor, si la vista de texto está vacía, restablezca su marcador de posición volviendo a agregar el texto del marcador de posición y configurando su color en gris claro.

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

Solución n. ° 2 : si desea que el marcador de posición se muestre siempre que la vista de texto esté vacía, incluso si la vista de texto está seleccionada:

Primero establezca el marcador de posición en viewDidLoad:

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

textView.becomeFirstResponder()

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

(Nota: dado que el OP quería tener seleccionada la vista de texto tan pronto como se carga la vista, incorporé la selección de vista de texto en el código anterior. Si este no es su comportamiento deseado y no desea que la vista de texto se seleccione al cargar la vista, elimine las dos últimas líneas del fragmento de código anterior).

Luego utilice el shouldChangeTextInRange UITextViewDelegatemétodo, así:

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
}

Y también implemente textViewDidChangeSelectionpara evitar que el usuario cambie la posición del cursor mientras el marcador de posición es visible. (Nota: textViewDidChangeSelectionse llama antes de que se cargue la vista, así que solo verifique el color de la vista de texto si la ventana está visible):

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

55
Hola, asegúrese de configurar su controlador de vista como delegado para su textView . Puede lograr esto creando una salida desde su textView a su viewController. Entonces usar yourTextField.delegate = self. Si no hace esto, textViewDidBeginEditinglas textViewDidEndEditingfunciones y no funcionarán.
Ujjwal-Nadhani

2
El código no se está compilando, estoy teniendo un error como Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter

44
He encontrado la solución y adjuntaré el código revisado @iPeter. El texto actual debe estar en NSString formantes: let currentText = textView.text as NSString?. Transforma la let updatedText =línea a let updatedText = currentText?.replacingCharacters(in: range, with: text). Finalmente, transforma la if updatedText.isEmptylínea en if (updatedText?.isEmpty)! {. ¡Eso debería hacer el truco!
Baylor Mitchell

1
@ TàTruhoada He actualizado la solución para manejar escenarios de copiar y pegar
Lyndsey Scott

3
La configuración de @LyndseyScott textView.selectedTextRangedesde dentro func textViewDidChangeSelection(_ textView: UITextView)provoca un bucle infinito ...
MikeG

229

Marcador de posición flotante


Es simple, seguro y confiable colocar una etiqueta de marcador de posición sobre una vista de texto, establecer su fuente, color y administrar la visibilidad del marcador de posición mediante el seguimiento de los cambios en el recuento de caracteres de la vista de texto.

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



19
Este método es sin duda el más simple y menos propenso a errores que he encontrado. Fabuloso.
David

66
Es un buen método, pero el texto de la etiqueta puede salirse del límite si el texto del marcador de posición es muy largo ...
Aks

55
Simple de usar y se integra muy bien con el comportamiento existente. El mensaje de marcador de posición no debería ser tan detallado de todos modos, sin embargo, ¿no establecer las líneas en 0 se ocuparía de ese problema?
Tommie C.

2
Generalmente prefiero este método, aunque es problemático si el texto está alineado en el centro porque el cursor estará centrado en la parte superior del marcador de posición en lugar de a la izquierda.
blwinters

1
@blwinters - Ese sería un caso de esquina realmente inusual, ¿no? Pero suponiendo que sea un campo de entrada de varias líneas en ese caso (tengo que asumir que), ¿no podría simplemente ajustar el cálculo de desplazamiento vertical para alejar un poco el texto del marcador de posición del cursor?
claro

35

Recomendamos encarecidamente utilizar la biblioteca KMPlaceholderTextView . Muy simple de usar.


1
Esta es, con mucho, la solución más fácil (no implica agregar UILabels o hacer cosas delegadas especiales). Funciona fuera de la caja.
HixField

Deberia de funcionar. El autor dijo que es compatible con Swift 3.0+
t4nhpt

27

Rápido:

Agregue su vista de texto mediante programación o mediante Interface Builder, si es el último, cree la salida:

@IBOutlet weak var yourTextView: UITextView!

Agregue el delegado (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

En el método viewDidLoad, agregue lo siguiente:

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

Ahora permítanme presentarles la parte mágica, agregue esta función:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Tenga en cuenta que esto se ejecutará cada vez que comience la edición, allí verificaremos las condiciones para indicar el estado, utilizando la propiedad de color. Establecer texto ennil no lo recomiendo. Justo después de eso, configuramos el color del texto al deseado, en este caso, negro.

Ahora agregue esta función también:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Permítanme insistir, no lo comparen nil, ya lo he intentado y no funcionaría. Luego establecemos los valores de nuevo al estilo de marcador de posición, y establecemos el color de nuevo al color de marcador de posición porque es una condición para registrarse textViewDidBeginEditing.


16

Utilice esta extensión, esta es la mejor manera de establecer un marcador de posición en UITextView. Pero asegúrese de haber adjuntado delegados a TextView. Puede configurar el marcador de posición de esta manera: -

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
Funcionó como encantos !! ¡Muchas gracias! :)
Nahid Raihan

Bienvenido Estimado Todo lo mejor
Sandip Gill

15

Me sorprende que nadie lo haya mencionado NSTextStorageDelegate. UITextViewDelegateLos métodos solo serán activados por la interacción del usuario, pero no mediante programación. Por ejemplo, cuando configura una vista de textotext propiedad de programación, tendrá que establecer la visibilidad del marcador de posición usted mismo, porque no se invocarán los métodos delegados.

Sin embargo, con NSTextStorageDelegateel textStorage(_:didProcessEditing:range:changeInLength:)método, se le notificará cualquier cambio en el texto, incluso si se realiza mediante programación. Simplemente asígnelo así:

textView.textStorage.delegate = self

(En UITextView, esta propiedad delegada es nilpredeterminada, por lo que no afectará ningún comportamiento predeterminado).

Combinarlo con la UILabeltécnica de @clearlight demuestra, uno puede fácilmente envolver todo el UITextView's placeholderaplicación en una extensión.

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

}

Tenga en cuenta que el uso de una clase privada (anidada) llamada PlaceholderLabel. No tiene ninguna implementación en absoluto, pero nos proporciona una forma de identificar la etiqueta de marcador de posición, que es mucho más 'rápida' que usar eltag propiedad.

Con este enfoque, aún puede asignar el delegado del UITextViewa otra persona.

Ni siquiera tiene que cambiar las clases de sus vistas de texto. Simplemente agregue las extensiones y podrá asignar una cadena de marcador de posición a cada UITextViewproyecto, incluso en el Creador de interfaces.

Dejé de lado la implementación de una placeholderColorpropiedad por razones de claridad, pero se puede implementar solo por unas pocas líneas más con una variable calculada similar a placeholder.


Solución muy elegante. Me encanta.
Dave Batton

textView.textStorage.delegate = selfesto en un controlador de vista requerirá que vinculemos con ese controlador de vista NSTextStorageDelegate. Realmente se requiere?
Hemang

Simple y funciona como un encanto. Esto debe ser aceptado respuesta
Qaiser Abbas

@Hemang es la vista de texto en sí misma NSTextStorageDelegate, no el controlador de vista.
yesleon

14

Lo hice usando dos vistas de texto diferentes:

  1. Uno en el fondo que se utiliza como marcador de posición.
  2. Uno en primer plano (con un fondo transparente) que el usuario realmente escribe.

La idea es que una vez que el usuario comienza a escribir cosas en la vista de primer plano, el marcador de posición en el fondo desaparece (y vuelve a aparecer si el usuario elimina todo). Por lo tanto, se comporta exactamente como un marcador de posición para el campo de texto de una sola línea.

Aquí está el código que usé para ello. Tenga en cuenta que descriptionField es el campo que escribe el usuario y descriptionPlaceholder es el que está en segundo plano.

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

1
De esta manera es un poco hacky, pero es de lejos el más fácil y genera exactamente los resultados que desea. Buena idea
William T.

9

Basado en algunas de las excelentes sugerencias aquí, pude reunir la siguiente subclase ligera, compatible con Interface-Builder UITextView, que:

  • Incluye texto de marcador de posición configurable, con un estilo similar al de UITextField.
  • No requiere subvistas o restricciones adicionales.
  • No requiere ninguna delegación u otro comportamiento del ViewController.
  • No requiere ninguna notificación.
  • Mantiene ese texto completamente separado de las clases externas que miran la textpropiedad del campo .

Cualquier sugerencia de mejora es bienvenida, especialmente si hay alguna forma de extraer el color del marcador de posición de iOS mediante programación, en lugar de 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

SET valor en carga 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

Traté de hacer el código conveniente a partir de la respuesta de clearlight .

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 par de problemas (1) Como una extensión que asume y 'toma prestada' un valor de propiedad de etiqueta UIView, existe el riesgo de que alguien use la misma etiqueta en su propia jerarquía de vistas, sin darse cuenta del uso de la extensión, creando un error extremadamente difícil de diagnosticar. Cosas como esas no pertenecen al código de la biblioteca ni a las extensiones. (2) Todavía requiere que la persona que llama declare un delegado. El marcador de posición flotante evita los hacks, tiene una huella pequeña, es simple y completamente local, lo que lo convierte en una apuesta segura.
clearlight

5

Una solución más (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)
    }
}

resultado

ingrese la descripción de la imagen aquí


4

Una solución simple y rápida que funciona para mí es:

@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

Esto es lo que estoy usando para este trabajo realizado.

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

}

Sé que hay varios enfoques similares a este, pero los beneficios de este son:

  • Puede establecer texto de marcador de posición, tamaño de fuente y color en IB .
  • Ya no muestra la advertencia de "La vista de desplazamiento tiene contenido desplazable ambiguo " en IB.
  • Agregue animación para mostrar / ocultar el marcador de posición.

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

Primero, cuando el usuario comienza a editar la llamada textViewDidBeginEditing y luego verifica si el color del texto gris significa que el usuario no escribió nada, luego establece como textview nil y cambia el color a negro para enviar mensajes de texto al usuario.

Cuando el usuario finaliza la edición de textViewDidEndEditing es una llamada y comprueba si el usuario no escribe nada en la vista de texto y luego el texto se establece en color gris con el texto "PlaceHolder"


3

Aquí está mi forma de resolver este problema ( Swift 4 ):

La idea era hacer la solución más simple posible que permita utilizar marcadores de posición de diferentes colores, cambiar el tamaño al tamaño de los marcadores de posición, no sobrescribir delegatemientras tanto, manteniendo todas las UITextViewfunciones funcionando como se esperaba.

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

Esta es una excelente y simple solución.
JaredH

2

No sé por qué las personas complican tanto este problema ... Es bastante sencillo y sencillo. Aquí hay una subclase de UITextView que proporciona la funcionalidad solicitada.

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

Para su información, antes de que alguien me gane ... esto es bastante directo para traducir a Swift. Nada complicado aquí.
TheCodingArt

2

Esta es mi solución lista para usar si está trabajando con múltiples vistas de texto

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 muy bonito!
KevinVuD

2

Swift 3.1

Esta extensión funcionó bien para mí: https://github.com/devxoul/UITextView-Placeholder

Aquí hay un fragmento de código:

Instalarlo a través de pod:

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

Importalo a tu clase

import UITextView_Placeholder

Y agregue placeholderpropiedad a su ya creadoUITextView

textView.placeholder = "Put some detail"

Eso es ... Así se ve (el tercer cuadro es a UITextView) ingrese la descripción de la imagen aquí


2

Al contrario de casi todas las respuestas de esta entrada, UITextView lo hace tener una propiedad de marcador de posición. Por razones más allá de mi comprensión, solo se expone en IB, como tal:

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

Entonces, si está utilizando guiones gráficos y un marcador de posición estático será suficiente, solo configure la propiedad en el inspector.

También puede establecer esta propiedad en un código como este:

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

Está nublado en cuanto al clima, se accede a través de una API privada, ya que la propiedad está expuesta.

No he intentado enviar con este método. Pero lo enviaré de esta manera en breve y actualizaré esta respuesta en consecuencia.

ACTUALIZAR:

He enviado este código en varias versiones sin problemas de Apple.

ACTUALIZACIÓN: Esto solo funcionará en Xcode pre 11.2


en Swift 5 puede escribir myTextView, marcador de posición = "ingrese su color de ojos"
user462990

@ user462990 ¿Puede proporcionar un enlace de documentación para esto? No creo que esto sea exacto.
Alex Chase el

lo siento, no encontré dox pero es realmente fácil de probar ... por ejemplo, "alert.addTextField {(textField3) en textField3.placeholder = language.JBLocalize (frase:" yourName ")}. jbLocalize es un administrador de traducción que devuelve un Cadena
usuario462990

44
@ user462990 Está haciendo referencia UITextFieldNO UITextView lea las preguntas / respuestas con más cuidado.
Alex Chase

3
Gran solución, pero no funciona para mí. De alguna manera siempre se bloquea. ¿Podría especificar las versiones de destino de despliegue, swift y xCode que está utilizando?
T. Pasichnyk

1

No existe tal propiedad en ios para agregar marcador de posición directamente en TextView, sino que puede agregar una etiqueta y mostrar / ocultar el cambio en textView. SWIFT 2.0 y asegúrese de implementar el textviewdelegate

func textViewDidChange(TextView: UITextView)
{

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

}

1

Swift: escribí una clase que heredó UITextView y agregué un UILabel como subvista para actuar como marcador de posición.

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

}

Gran ejemplo del uso de restricciones para colocar el marcador de posición, sin embargo, la posición ideal del marcador de posición está justo arriba de donde irá el primer carácter del texto, por lo que calcular esa posición en función de la fuente de la vista de texto es mejor porque se adapta a cualquier tamaño de fuente establecido para la vista de texto.
Clearlight

1

Me gusta la solución de @ nerdist. Basado en eso, creé una extensión para 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
    }
  }
}

En una clase ViewController, por ejemplo, así es como 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)
    ...
  }

Marcador de posición en UITextview!

ingrese la descripción de la imagen aquí

ACTUALIZACIÓN :

En caso de que cambie el texto de la vista de texto en el código, recuerde llamar al método updateVisibitly para ocultar el marcador de posición:

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

Para evitar que el marcador de posición se agregue más de una vez, add()se agrega una función privada extension.


Gracias de nuevo por su mejora al original. Pasé mucho tiempo en esto este fin de semana, pero creo que disfrutarán de la variante extrema EZ Placeholder que eventualmente se me ocurrió: stackoverflow.com/a/41081244/2079103 (Le di crédito por contribuir a la evolución de la soluciones de marcador de posición)
clearlight

1

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

}

En swift3:

import UIKit

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

}

Escribí una clase en Swift. Debe importar esta clase cuando sea necesario.


Mencione para qué versión de Swift es (no funciona para Swift 3)
clearlight

1

No puedo agregar comentarios debido a la reputación. agregue una necesidad de delegado más en la respuesta @clearlight.

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

es necesidad

porque textViewDidChangeno se llama la primera vez


1

no, no hay ningún marcador de posición disponible para la vista de texto. debe colocar una etiqueta encima cuando el usuario ingresa en la vista de texto y luego ocultarlo o establecerlo por defecto cuando el usuario ingresa eliminar todos los valores.


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

Tuve que enviar la cola para que mi texto de marcador de posición reapareciera una vez que se completara la edición.

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

¿Qué escribo en lugar de la última línea "textView.text =" Descripción "si quiero que este valor sea lo que el usuario está escribiendo?
ISS

1

Rápido:

Agrega tu TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

En el viewWillAppearmétodo, agregue lo siguiente:

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

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

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

}

Agregue la Delegateextensión Usando (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

Nuestra solución evita el robo con las propiedades UITextView texty textColor, lo cual es útil si mantiene un contador de caracteres.

Es sencillo:

1) Cree un maniquí UITextViewen Storyboard con las mismas propiedades que el maestro UITextView. Asigne texto de marcador de posición al texto ficticio.

2) Alinee los bordes superior, izquierdo y derecho de los dos UITextViews.

3) Coloque el maniquí detrás del maestro.

4) Anule la textViewDidChange(textView:)función de delegado del maestro y muestre el ficticio si el maestro tiene 0 caracteres. De lo contrario, muestra al maestro.

Esto supone que ambos UITextViewstienen fondos transparentes. Si no lo hacen, coloque el maniquí en la parte superior cuando haya 0 caracteres y empújelo debajo cuando haya> 0 caracteres. También deberá intercambiar los respondedores para asegurarse de que el cursor siga a la derecha UITextView.


1

Swift 4, 4.2 y 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]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.