Como adicionar texto de espaço reservado dentro de um UITextView no Swift?

316

Estou fazendo um aplicativo que usa a UITextView. Agora, quero que a exibição de texto tenha um espaço reservado semelhante ao que você pode definir para um campo de texto. Como você faria isso usando o Swift?

StevenR
fonte
Este é um problema antigo no desenvolvimento do iOS com o UITextView. Eu escrevi subclasses como a mencionada aqui: stackoverflow.com/a/1704469/1403046 . O benefício é que você ainda pode ter um representante, além de usar a classe em vários locais sem precisar reimplementar a lógica.
cjwirth
Como eu usaria sua subclasse, enquanto usava o swift para o projeto. Usando um arquivo de ponte?
StevenR
Você pode fazer isso ou reimplementá-lo no Swift. O código na resposta é mais longo do que realmente precisa ser. O ponto principal é mostrar / ocultar o rótulo que você adiciona no método para o qual você é notificado quando o texto muda.
cjwirth
Você pode usar o exemplo UIFloatLabelTextView do GitHub. Este espaço reservado na posição em cima enquanto escreve. Realmente interessante! github.com/ArtSabintsev/UIFloatLabelTextView
Jayprakash Dubey
2
Honestamente, a maneira mais fácil de fazer isso é ter um textView personalizado e apenas adicionar um espaço reservado ao texto que é desenhado no textView quando nenhum texto está presente ... Qualquer outra resposta até agora tem sido uma versão muito complicada disso que envolve problemas gerenciamento de estado (incluindo falsos positivos para quando o texto deverá / não / não existe)
TheCodingArt

Respostas:

649

Atualizado para o Swift 4

UITextViewnão possui inerentemente uma propriedade de espaço reservado, então você precisa criar e manipular uma programaticamente usando UITextViewDelegatemétodos. Eu recomendo usar a solução 1 ou 2 abaixo, dependendo do comportamento desejado.

Nota: Para qualquer solução, adicione UITextViewDelegateà classe e configure textView.delegate = selfpara usar os métodos de delegação da exibição de texto.


Solução 1 - Se você desejar que o espaço reservado desapareça assim que o usuário selecionar a exibição de texto:

Primeiro, defina o UITextViewpara conter o texto do espaço reservado e defina-o com uma cor cinza claro para imitar a aparência do UITextFieldtexto do espaço reservado. Faça isso na viewDidLoadcriação da exibição de texto ou após ela.

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

Então, quando o usuário começar a editar a exibição de texto, se ela contiver um espaço reservado (por exemplo, se a cor do texto estiver em cinza claro), limpe o espaço reservado e defina a cor do texto para preto para acomodar a entrada do usuário.

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

Em seguida, quando o usuário terminar de editar a exibição de texto e ela for renunciada como a primeira resposta, se a exibição de texto estiver vazia, redefina seu espaço reservado adicionando novamente o texto do espaço reservado e definindo sua cor para cinza claro.

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

Solução 2 - Se você deseja que o espaço reservado seja exibido sempre que a exibição de texto estiver vazia, mesmo que a exibição de texto esteja selecionada:

Primeiro, defina o espaço reservado no viewDidLoad:

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

textView.becomeFirstResponder()

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

(Observação: como o OP queria que a visualização de texto fosse selecionada assim que a visualização fosse carregada, incorporei a seleção da visualização de texto no código acima. Se esse não é o seu comportamento desejado e você não deseja que a visualização de texto seja selecionada após o carregamento da visualização, remova as duas últimas linhas do bloco de código acima.)

Em seguida, utilize o shouldChangeTextInRange UITextViewDelegatemétodo, assim:

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 também implemente textViewDidChangeSelectionpara impedir que o usuário altere a posição do cursor enquanto o espaço reservado estiver visível. (Nota: textViewDidChangeSelectioné chamado antes que a visualização seja carregada, verifique somente a cor da visualização de texto se a janela estiver visível):

func textViewDidChangeSelection(_ textView: UITextView) {
    if self.view.window != nil {
        if textView.textColor == UIColor.lightGray {
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
    }
}
Lyndsey Scott
fonte
5
Oi, certifique-se de definir o seu controlador de exibição como o delegado para o seu textView . Você pode conseguir isso criando uma saída do textView para o viewController. Então use yourTextField.delegate = self. Se você não fizer isso, textViewDidBeginEditingas textViewDidEndEditingfunções e não funcionarão.
Ujjwal-Nadhani
2
O código não está compilando, estou tendo um erro como Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter 27/06
4
Encontrei a solução e anexarei o código revisado @iPeter. O texto atual deve estar em NSString formante: let currentText = textView.text as NSString?. Transforme a let updatedText =linha em let updatedText = currentText?.replacingCharacters(in: range, with: text). Por fim, transforme a if updatedText.isEmptylinha em if (updatedText?.isEmpty)! {. Isso deve fazer o truque!
Baylor Mitchell
1
@ TàTruhoada Eu atualizei a solução para cópia punho e cole cenários
Lyndsey Scott
3
Definição @LyndseyScott textView.selectedTextRangede dentro func textViewDidChangeSelection(_ textView: UITextView)faz com que um loop infinito ...
MikeG
229

Espaço reservado flutuante


É simples, seguro e confiável posicionar um rótulo de espaço reservado acima de uma exibição de texto, definir sua fonte, cor e gerenciar a visibilidade do espaço reservado, acompanhando as alterações na contagem de caracteres da exibição 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: mesmo, exceto: italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor


luz clara
fonte
19
Este método é inegavelmente o mais simples e menos propenso a erros que eu encontrei. Fabuloso.
David
6
É um método bom, mas o texto da etiqueta pode sair do vinculados se o texto espaço reservado é muito longa ..
Aks
5
Simples de usar e integra-se perfeitamente ao comportamento existente. A mensagem do espaço reservado não deve ser tão detalhada de qualquer maneira, no entanto, não definir as linhas como 0 cuidará desse problema?
Tommie C.
2
Geralmente, eu prefiro esse método, embora seja problemático se o texto estiver alinhado ao centro, porque o cursor será centralizado na parte superior do espaço reservado, em vez de à esquerda dele.
blwinters
1
@blwinters - Esse seria um caso de canto realmente incomum, não? Mas, supondo que seja um campo de entrada de várias linhas nesse caso (eu tenho que assumir isso), você não poderia simplesmente ajustar o cálculo do deslocamento vertical para mover um pouco o texto do espaço reservado para fora do cursor?
clearlight
35

É altamente recomendável usar a biblioteca KMPlaceholderTextView . Muito simples de usar.

t4nhpt
fonte
1
Essa é de longe a solução mais fácil (não envolve adicionar UILabels ou executar tarefas especiais de delegação). Trabalha fora da caixa.
HixField 27/08/16
Deveria funcionar. O autor disse que suporta o Swift 3.0+
t4nhpt 17/17/17
27

Rápido:

Adicione sua exibição de texto programaticamente ou via Interface Builder, se for o último, crie a saída:

@IBOutlet weak var yourTextView: UITextView!

Por favor, adicione o delegado (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

No método viewDidLoad, adicione o seguinte:

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

Agora, deixe-me apresentar a parte mágica, adicione esta função:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Observe que isso será executado sempre que a edição for iniciada. Lá, verificaremos as condições para informar o estado, usando a propriedade color. Definir o texto para nileu não recomendo. Logo depois disso, definimos a cor do texto como desejada, neste caso, preto.

Agora adicione também esta função:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Deixe-me insistir, não compare com nil, eu já tentei isso e não funcionaria. Em seguida, definimos os valores de volta ao estilo do espaço reservado e a cor de volta à cor do espaço reservado, pois é uma condição para fazer check-in textViewDidBeginEditing.

Juan Boero
fonte
16

Use This Extension, é a melhor maneira de definir espaço reservado no UITextView. Mas verifique se anexou delegados ao TextView. Você pode definir o espaço reservado desta forma: -

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
    }
}
Sandip Gill
fonte
1
Trabalhou como encantos !! Muito obrigado! :)
Nahid Raihan
Bem-vindo Caro, tudo de bom
Sandip Gill
15

Estou surpreso que ninguém tenha mencionado NSTextStorageDelegate. UITextViewDelegateOs métodos de apenas serão acionados pela interação do usuário, mas não programaticamente. Por exemplo, quando você define a textpropriedade de uma exibição de texto de forma programática, terá que definir a visibilidade do espaço reservado, porque os métodos delegados não serão chamados.

No entanto, com NSTextStorageDelegateo textStorage(_:didProcessEditing:range:changeInLength:)método, você será notificado sobre qualquer alteração no texto, mesmo que seja feita de forma programática. Basta atribuir assim:

textView.textStorage.delegate = self

(Em UITextView, essa propriedade delegada é nilpor padrão, não afeta nenhum comportamento padrão.)

Combiná-lo com a UILabeltécnica @clearlight demonstra, pode-se facilmente envolver toda a UITextView's placeholderimplementação em uma extensão.

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

}

Observe que o uso de uma classe privada (aninhada) é chamada PlaceholderLabel. Ele não tem nenhuma implementação, mas fornece uma maneira de identificar o rótulo do espaço reservado, que é muito mais 'rápido' do que usar a tagpropriedade.

Com essa abordagem, você ainda pode atribuir o delegado da a UITextViewoutra pessoa.

Você nem precisa alterar as classes das visualizações de texto. Basta adicionar a (s) extensão (ões) e você poderá atribuir uma sequência de marcadores a cada um UITextViewno seu projeto, mesmo no Interface Builder.

Eu deixei de fora a implementação de uma placeholderColorpropriedade por motivos de clareza, mas ela pode ser implementada por apenas mais algumas linhas com uma variável computada semelhante a placeholder.

yesleon
fonte
Solução muito elegante. Eu amo isso.
Dave Batton
textView.textStorage.delegate = selfisso em um controlador de exibição exigirá que vinculemos esse controlador de exibição NSTextStorageDelegate. É realmente requer?
Hemang 8/08/19
Simples e funciona como um encanto. Este deve ser aceito resposta
Qaiser Abbas
@ Hemang é o próprio modo de exibição de texto NSTextStorageDelegate, não o controlador de exibição.
yesleon 19/09/19
14

Eu fiz isso usando duas visualizações de texto diferentes:

  1. Um em segundo plano que é usado como um espaço reservado.
  2. Um em primeiro plano (com um plano de fundo transparente) que o usuário digita.

A idéia é que, assim que o usuário começar a digitar itens na exibição em primeiro plano, o espaço reservado em segundo plano desaparecerá (e reaparecerá se o usuário excluir tudo). Portanto, ele se comporta exatamente como um espaço reservado para o campo de texto de linha única.

Aqui está o código que eu usei para isso. Observe que descriptionField é o campo em que o usuário digita e descriptionPlaceholder é o campo em segundo plano.

func textViewDidChange(descriptionField: UITextView) {
    if descriptionField.text.isEmpty == false {
        descriptionPlaceholder.text = ""
    } else {
        descriptionPlaceholder.text = descriptionPlaceholderText
    }
}
yesthisisjoe
fonte
1
Dessa forma, é um pouco invasivo, mas de longe é o mais fácil e gera exatamente os resultados desejados. Boa ideia
William T.
9

Com base em algumas das excelentes sugestões já apresentadas, pude reunir a seguinte subclasse leve e compatível com o Interface Builder UITextView, que:

  • Inclui texto de espaço reservado configurável, com o mesmo estilo de UITextField.
  • Não requer subvisões ou restrições adicionais.
  • Não requer nenhuma delegação ou outro comportamento do ViewController.
  • Não requer nenhuma notificação.
  • Mantém esse texto totalmente separado de qualquer classe externa que observe a textpropriedade do campo .

Quaisquer sugestões de melhoria são bem-vindas, especialmente se houver alguma maneira de extrair a cor do espaço reservado do iOS por meio de programação, em vez de codificá-la.

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
    }
}
TheNeil
fonte
6

Valor SET no carregamento da 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()
}
user2991582
fonte
6

Tentei tornar o código conveniente clearlight 's resposta .

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()
}
Leonardo
fonte
Algumas questões. (1) Como uma extensão que assume e 'empresta' um valor de propriedade da tag UIView, existe o risco de alguém usar a mesma tag em sua própria hierarquia de exibição, sem conhecer o uso da extensão, criando um bug extremamente difícil de diagnosticar. Coisas assim não pertencem ao código ou extensões da biblioteca. (2) Ainda requer que o chamador declare um delegado. o reservado flutuante evita hacks, tem uma pegada minúscula, é simples e totalmente local, o que o torna uma aposta segura.
clearlight 28/02
5

Mais uma solução (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

insira a descrição da imagem aqui

gbk
fonte
4

Uma solução simples e rápida que funciona para mim é:

@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
    }
}
Sepand Y.
fonte
4

Aqui está o que estou usando para este trabalho.

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

}

Eu sei que existem várias abordagens semelhantes a essa, mas os benefícios dessa são:

  • Pode definir texto de espaço reservado, tamanho da fonte e cor no IB .
  • Não mostra mais o aviso de " Exibição de rolagem com conteúdo rolável ambíguo " no IB.
  • Adicione animação para mostrar / ocultar o espaço reservado.
Pei
fonte
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
        }
    }
}

Primeiro, quando o usuário começa a editar a chamada textViewDidBeginEditing e, em seguida, verifica se a cor cinza do texto significa que o usuário não escreveu nada; depois, definiu como nulo e altere a cor para preto para as mensagens de texto do usuário.

Quando o usuário final editar textViewDidEndEditing é chamar e verificar se o usuário não escreve nada no textview, o texto é definido como cor cinza com o texto "PlaceHolder"

VD Purohit
fonte
3

Aqui está a minha maneira de resolver este problema ( Swift 4 ):

A idéia era tornar a solução mais simples possível, que permite usar espaços reservados de cores diferentes, redimensionar para o tamanho dos espaços reservados, não sobrescreverá delegateenquanto isso, mantendo todas as UITextViewfunções funcionando conforme o esperado.

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) }
}
poucas linhas de código
fonte
Esta é uma solução excelente e simples.
JaredH
2

Não sei por que as pessoas complicam tanto esse problema ... É bastante direto e simples. Aqui está uma subclasse de UITextView que fornece a funcionalidade 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];
    }`
TheCodingArt
fonte
Para sua informação, antes que alguém me derrube ... isso é bastante direto para traduzir para Swift. Nada complicado aqui.
TheCodingArt
2

Esta é a minha solução pronta para uso, se você estiver trabalhando com várias visualizações 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
    }
}
nickoff
fonte
É muito legal!
KevinVuD
2

Swift 3.1

Esta extensão funcionou bem para mim: https://github.com/devxoul/UITextView-Placeholder

Aqui está um trecho de código:

Instale-o via pod:

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

Importá-lo para sua turma

import UITextView_Placeholder

E adicione placeholderpropriedade ao seu já criadoUITextView

textView.placeholder = "Put some detail"

É isso aí ... Aqui está a aparência (a terceira caixa é a UITextView) insira a descrição da imagem aqui

Vaibhav Saran
fonte
2

Ao contrário do que apenas sobre cada resposta sobre este post, UITextView faz tem uma propriedade do espaço reservado. Por razões além da minha compreensão, ele é exposto apenas no IB, como tal:

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

Portanto, se você estiver usando storyboards e um espaço reservado estático será suficiente, basta definir a propriedade no inspetor.

Você também pode definir esta propriedade no código como este:

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

Sua nebulosidade quanto ao clima é acessada via API privada, pois a propriedade é exposta.

Não tentei enviar com esse método. Mas enviarei esse caminho em breve e atualizarei essa resposta.

ATUALIZAR:

Eu enviei esse código em várias versões sem problemas da Apple.

UPDATE: Isso funcionará apenas no Xcode pré 11.2

Alex Chase
fonte
em Swift 5 você pode escrever myTextView, espaço reservado = "introduzir a sua cor dos olhos"
user462990
@ user462990 Você pode fornecer um link de documentação para isso? Não acredito que isso seja preciso.
Alex perseguição
desculpe, não encontrei dox, mas é realmente fácil testar ... por exemplo, "alert.addTextField {(textField3) em textField3.placeholder = language.JBLocalize (frase:" yourName ")}. jbLocalize é um gerenciador de tradução que retorna um String
user462990
4
@ user462990 Você está referenciando UITextFieldNÃO, UITextView por favor leia as perguntas / respostas com mais cuidado.
Alex Chase
3
Ótima solução, mas não funciona para mim. De alguma forma, sempre falha. Você poderia especificar o destino de implantação, as versões swift e xCode que está usando?
T. Pasichnyk
1

Não existe essa propriedade no ios para adicionar espaço reservado diretamente no TextView. Em vez disso, você pode adicionar um rótulo e mostrar / ocultar a alteração no textView. SWIFT 2.0 e certifique-se de implementar o textviewdelegate

func textViewDidChange(TextView: UITextView)
{

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

}
Himanshu
fonte
1

Swift - escrevi uma classe que herdou o UITextView e adicionei um UILabel como uma subview para atuar como um espaço reservado.

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

}
nabil muthanna
fonte
Ótimo exemplo de uso de restrições para posicionar o espaço reservado, no entanto, a posição ideal do espaço reservado fica logo acima do local do primeiro caractere do texto, portanto, calcular essa posição com base na fonte da exibição de texto é melhor porque adapta-se para qualquer tamanho de fonte definido para a exibição de texto.
clearlight
1

Eu gosto da solução do @ nerdist. Com base nisso, criei uma extensão 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
    }
  }
}

Em uma classe ViewController, por exemplo, é assim que eu a 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)
    ...
  }

Espaço reservado no UITextview!

insira a descrição da imagem aqui

ATUALIZAR :

Caso você altere o texto do textview no código, lembre-se de chamar o método updateVisibitly para ocultar o espaço reservado:

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

Para impedir que o espaço reservado seja adicionado mais de uma vez, uma add()função privada é adicionada extension.

David.Chu.ca
fonte
Agradecemos novamente por sua melhoria no original. Passei muito tempo neste final de semana, mas acho que você apreciará a variante extrema do EZ Placeholder que acabei criando: stackoverflow.com/a/41081244/2079103 (eu lhe dei crédito por contribuir com a evolução do placeholder solutions)
clearlight 12/12/16
1

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

}

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

}

Eu escrevi uma aula rapidamente. Você precisa importar esta classe sempre que necessário.

Sujatha Girijala
fonte
Mencione qual é a versão do Swift (não funciona para o Swift 3)
clearlight
1

Não posso adicionar comentários por causa da reputação. adicione mais uma necessidade de delegado na resposta do @clearlight.

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

é preciso

porque textViewDidChangenão é chamado pela primeira vez

Seungyoun Yi
fonte
1

não, não existe nenhum espaço reservado para visualização de texto. você deve colocar o rótulo acima dele quando o usuário digitar no modo de exibição de texto e depois ocultá-lo ou definir o valor padrão quando o usuário digitar remover todos os valores.


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

Saurabh Sharma
fonte
1

Eu tive que despachar a fila para que o texto do meu espaço reservado reaparecesse assim que a edição fosse concluída.

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"
        }
    }
}
Taylor A. Leach
fonte
O que digito em vez da última linha ”textView.text =” Descrição ”se quiser que esse valor seja o que o usuário está digitando?
ISS
1

Rápido:

Adicione o seu TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

No viewWillAppearmétodo, adicione o seguinte:

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

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

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

}

Por favor, adicione a Delegateextensão 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
        }
    }
}
Trupesh Vaghasiya
fonte
1

Nossa solução evita mexer com as propriedades UITextView texte textColor, o que é útil se você estiver mantendo um contador de caracteres.

É simples:

1) Crie um manequim UITextViewno Storyboard com as mesmas propriedades que o mestreUITextView . Atribua o texto do espaço reservado ao texto fictício.

2) Alinhe as bordas superior, esquerda e direita dos dois UITextViews.

3) Coloque o manequim atrás do mestre.

4) Substitua o textViewDidChange(textView:) função delegada do mestre e mostre o manequim se o mestre tiver 0 caracteres. Caso contrário, mostre o mestre.

Isso pressupõe que ambos UITextViewstenham planos de fundo transparentes. Caso contrário, coloque o boneco em cima quando houver 0 caracteres e empurre-o por baixo quando houver> 0 caracteres. Você também precisará trocar os respondedores para garantir que o cursor siga à direita UITextView.

Crashalot
fonte
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]
Akbar Khan
fonte