Como verifico quando um UITextField é alterado?

290

Estou tentando verificar quando um campo de texto é alterado, equivalente também à função usada para textView - textViewDidChangeaté agora, fiz isso:

  func textFieldDidBeginEditing(textField: UITextField) {
        if self.status.text == "" && self.username.text == "" {
            self.topRightButton.enabled = false
        } else {   
            self.topRightButton.enabled = true
        }
    }

Que tipo de trabalho, mas topRightButtonestá ativado assim que o campo de texto é pressionado, desejo que seja ativado apenas quando o texto é realmente digitado?

boraseoksoon
fonte

Respostas:

739

RÁPIDO

Swift 4.2

textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

e

@objc func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 3 e Swift 4.1

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

e

func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 2.2

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)

e

func textFieldDidChange(textField: UITextField) {
    //your code
}

OBJETIVO-C

[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

e o método textFieldDidChange é

-(void)textFieldDidChange :(UITextField *) textField{
    //your code
}
Fawad Masud
fonte
Isso trava para mim e eu não entendo o porquê.
Levi Roberts
1
Verificado várias vezes. O delegado é definido imediatamente antes dele viewDidLoad. A ação é letra por letra da mesma forma. O aplicativo falha assim que um botão do teclado é pressionado. Edit: Descobri isso! Faltava o ponto e vírgula dentro da ação. Eu supus que só tinha que ser o mesmo que o nome da função.
Levi Roberts
@FawadMasud isso não faz nada agora no Swift 2.0 no iOS 9 com XCode 7, foi depreciado ou você conhece a maneira atual de corrigi-lo?
Cody Weaver
1
@ bibscy sim, você precisa percorrer todos os campos de texto dentro de uma visualização.
Fawad Masud
1
Para o Swift 4.2: Texttfield.addTarget (self, action: #selector (ViewControllerr.textFieldDidChange (_ :)), para: UIControl.Event.editingChanged)
Saia de
128

Você pode fazer essa conexão no construtor de interface.

  1. No seu storyboard, clique no editor assistente na parte superior da tela (dois círculos no meio). Editor assistente selecionado

  2. Ctrl + Clique no campo de texto no construtor de interfaces.

  3. Arraste de EditingChanged para dentro da classe do controlador de exibição na exibição do assistente. Fazendo conexão

  4. Nomeie sua função ("textDidChange", por exemplo) e clique em conectar. Função de nomeação

rmooney
fonte
3
Essa é uma ótima solução, especialmente se estiver lidando com um UITextField em um tableViewCell que está sendo gerenciado por uma fonte de dados separada. Essa abordagem permite que o viewController responda diretamente (portanto, a fonte de dados não precisa responder e delegar a ação).
Wf810
1
Ótimo - uma solução simples para um problema irritante. Obviamente, você pode vincular vários campos de texto
Jeremy Andrews
1
Provavelmente, uma resposta melhor que a anterior, porque a eliminação da adição de @objc func.
Matthew Bradshaw
Boa idéia, eu uso o evento DidEndEditing
Puji Wahono
essa é a melhor solução. Obrigado @rmooney!
Jonathan
63

Swift 5.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: .editingChanged)

e manipular método:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 4.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

e manipular método:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 3.0

textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)

e manipular método:

func textFieldDidChange(textField: UITextField) { 

}
Robert
fonte
29

A maneira como eu lidei com isso até agora: em UITextFieldDelegate

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
    // text hasn't changed yet, you have to compute the text AFTER the edit yourself
    let updatedString = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)

    // do whatever you need with this updated string (your code)


    // always return true so that changes propagate
    return true
}

Versão Swift4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
    return true
}
Vinzzz
fonte
1
Isso não será chamado quando um campo de texto estiver vazio e o usuário clicar em backspace.
Matthew Mitchell
14

Swift 3

 textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(sender:)), for: UIControlEvents.editingChanged)
Alessandro Mattiuzzi
fonte
7

Swift 3.0.1+ (Algumas das outras respostas do Swift 3.0 não estão atualizadas)

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

func textFieldDidChange(_ textField: UITextField) {

}
aviran
fonte
6

O textField (_: shouldChangeCharactersIn: ReplacementString :) funcionou para mim no Xcode 8, Swift 3, se você quiser verificar cada pressionamento de tecla.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    // Whatever code you want to run here.
    // Keep in mind that the textfield hasn't yet been updated,
    // so use 'string' instead of 'textField.text' if you want to
    // access the string the textfield will have after a user presses a key

    var statusText = self.status.text
    var usernameText = self.username.text

    switch textField{
    case self.status:
        statusText = string
    case self.username:
        usernameText = string
    default:
        break
    }

    if statusText == "" && usernameText == "" {
        self.topRightButton.enabled = false
    } else {   
        self.topRightButton.enabled = true
    }

    //Return false if you don't want the textfield to be updated
    return true
}
radthemad4
fonte
5

Swift 4

Conformidade com UITextFieldDelegate .

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // figure out what the new string will be after the pending edit
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)

    // Do whatever you want here


    // Return true so that the change happens
    return true
}
drewster
fonte
4

Você pode usar este método delegado de UITextFieldDelegate. É acionado a cada mudança de personagem.

(Objective C) textField:shouldChangeCharactersInRange:replacementString:
(Swift) textField(_:shouldChangeCharactersInRange:replacementString:)

No entanto, isso só acontece antes que uma alteração seja feita (na verdade, uma alteração é feita apenas se você retornar verdadeiro a partir daqui).

Abubakr Dar
fonte
1
Como isso deve ser escrito, como eu também tentei esse método e chegue à mesma solução, onde ele muda apenas quando o textField é ativado, e não quando o texto realmente muda?
Quando você implementa o método delegado acima, ele é acionado toda vez que você altera seu texto. Você só precisa adicionar este código, self.textfield.delegate = self
Abubakr Dar
Para mim, esse método não funcionou porque você não pôde verificar se o campo de texto estava vazio dentro do método. Principalmente porque retorna verdadeiro / falso, dependendo se o campo de texto puder mudar. Portanto, o evento é acionado ANTES do campo de texto ter tido a chance de ficar vazio.
Levi Roberts
@LeviRoberts, você tem uma referência ao campo de texto dentro deste método. Assim, você pode verificar se o campo de texto. Texto está vazio.
Abubakr Dar
Você parece não entender. Quando está vazio, o .isEmptymétodo não equivale a verdadeiro até que APÓS este método tenha tido a chance de retornar verdadeiro; para informar ao aplicativo que o campo de texto deve mudar.
Levi Roberts
3

Talvez use RxSwift?

necessidade

pod 'RxSwift',    '~> 3.0'
pod 'RxCocoa',    '~> 3.0'

adicionar importações obviamente

import RxSwift
import RxCocoa

Então você tem um textfield : UITextField

let observable: Observable<String?> = textField.rx.text.asObservable()
observable.subscribe(
            onNext: {(string: String?) in
                print(string!)
        })

Você tem outros 3 métodos ..

  1. onError
  2. onCompleted
  3. onDisposed
  4. onNext
marlonpya
fonte
Para receber apenas eventos de mudança real e não também quando o campo de texto se tornou o primeiro respondedor, é necessário usar oUnitUnChanged no texto.
RealNmae 10/11/19
1

Swift 4

textField.addTarget(self, action: #selector(textIsChanging), for: UIControlEvents.editingChanged)

@objc func textIsChanging(_ textField:UITextField) {

 print ("TextField is changing")

}

Se você quiser fazer uma alteração depois que o usuário digitar completamente (será chamado assim que o usuário desconectar o teclado ou pressionar enter).

textField.addTarget(self, action: #selector(textDidChange), for: UIControlEvents.editingDidEnd)

 @objc func textDidChange(_ textField:UITextField) {

       print ("TextField did changed") 
 }
Anamika
fonte
1
txf_Subject.addTarget(self, action:#selector(didChangeFirstText), for: .editingChanged)

@objc func didChangeText(textField:UITextField) {
    let str = textField.text
    if(str?.contains(" "))!{
        let newstr = str?.replacingOccurrences(of: " ", with: "")
        textField.text = newstr
    }
}

@objc func didChangeFirstText(textField:UITextField) {
    if(textField.text == " "){
        textField.text = ""
    }
}
Yogesh Tandel
fonte
1

Você deve seguir estas etapas:

  1. Faça uma referência de saída para o campo de texto
  2. AssignUITextFieldDelegate à classe do controlador
  3. Configure yourTextField.delegate
  4. Implemente qualquer função que você precise

Código de amostra:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var yourTextFiled : UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        yourTextFiled.delegate = self
    }


    func textFieldDidEndEditing(_ textField: UITextField) {
        // your code
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // your code
    }

    .
    .
    .
}
M.Hazara
fonte
0

É assim que você pode adicionar um Swift 3textField text change listener usando :

Declare sua classe como UITextFieldDelegate

override func viewDidLoad() {
    super.viewDidLoad()

    textField.delegate = self

    textField.addTarget(self, action: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:)), for: UIControlEvents.editingChanged)
}

Em seguida, basta adicionar tradicionalmente uma função textFieldShouldEndEditing:

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { // do stuff
        return true 
}
Codetard
fonte
0

Swift 4.2

escreva isso em viewDidLoad

// to detect if TextField changed
TextField.addTarget(self, action: #selector(textFieldDidChange(_:)),
                                   for: UIControl.Event.editingChanged)

escreva isso fora viewDidLoad

@objc func textFieldDidChange(_ textField: UITextField) {
    // do something
}

Você pode alterar o evento usando UIControl.Event.editingDidBegin ou o que você quiser detectar.

gênio
fonte
0

Caso você esteja interessado em uma solução SwiftUI, isso está funcionando para mim:

 TextField("write your answer here...",
            text: Binding(
                     get: {
                        return self.query
                       },
                     set: { (newValue) in
                        self.fetch(query: newValue) // any action you need
                                return self.query = newValue
                      }
            )
  )

Eu tenho que dizer que não é minha ideia, eu li neste blog: SwiftUI binding: Um truque muito simples

abanet
fonte
0

Caso não seja possível vincular o addTarget ao seu UITextField, aconselho a vincular um deles, conforme sugerido acima, e insira o código para execução no final do método shouldChangeCharactersIn.

nameTextField.addTarget(self, action: #selector(RegistrationViewController.textFieldDidChange(_:)), for: .editingChanged)

@objc func textFieldDidChange(_ textField: UITextField) {
    if phoneNumberTextField.text!.count == 17 && nameTextField.text!.count > 0 {
        continueButtonOutlet.backgroundColor = UIColor(.green)
    } else {
        continueButtonOutlet.backgroundColor = .systemGray
    }
}

E na chamada shouldChangeCharactersIn func.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    guard let text = textField.text else {
        return true
    }
    let lastText = (text as NSString).replacingCharacters(in: range, with: string) as String

    if phoneNumberTextField == textField {
        textField.text = lastText.format("+7(NNN)-NNN-NN-NN", oldString: text)
        textFieldDidChange(phoneNumberTextField)
        return false
    }
    return true
}
Степан Варткинаян
fonte
-1

veloz 4

Em viewDidLoad ():

    //ADD BUTTON TO DISMISS KEYBOARD

    // Init a keyboard toolbar 
    let toolbar = UIView(frame: CGRect(x: 0, y: view.frame.size.height+44, width: view.frame.size.width, height: 44))
    toolbar.backgroundColor = UIColor.clear

    // Add done button
    let doneButt = UIButton(frame: CGRect(x: toolbar.frame.size.width - 60, y: 0, width: 44, height: 44))
    doneButt.setTitle("Done", for: .normal)
    doneButt.setTitleColor(MAIN_COLOR, for: .normal)
    doneButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 13)
    doneButt.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
    toolbar.addSubview(doneButt)

    USDTextField.inputAccessoryView = toolbar

Adicione esta função:

    @objc func dismissKeyboard() {
      //Causes the view (or one of its embedded text fields) to resign the first responder status.
      view.endEditing(true)
    }
Nada
fonte
-1

crie uma nova classe personalizada MaterialTextfield.swift

class MaterialTextfield: UITextField,UITextFieldDelegate {

var bottomBorder = UIView()
var shouldShowEditing = false

override func awakeFromNib() {

    // Setup Bottom-Border

    self.delegate = self
    self.translatesAutoresizingMaskIntoConstraints = false

    bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
    bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1) // Set Border-Color
    bottomBorder.translatesAutoresizingMaskIntoConstraints = false

    addSubview(bottomBorder)

    bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength

}
@IBInspectable var hasError: Bool = false {
    didSet {
        if (hasError) {
            bottomBorder.backgroundColor = UIColor.red//error color
        } else {
            bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)//passive color
        }

    }
}
@IBInspectable var showEditing: Bool = false{
    didSet {
        if (showEditing) {
            bottomBorder.backgroundColor = UIColor(rgb: 0x56B5CA)//active color
        } else {
            bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)//passive color
        }

    }

}

func textFieldDidBeginEditing(_ textField: UITextField) {//listen to on edit event
    showEditing = !self.showEditing
}
func textFieldDidEndEditing(_ textField: UITextField) {//listen to on end edit event
    showEditing = !self.showEditing
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {//listen to return button event
    textField.resignFirstResponder() // return button will close keyboard
    return true
}

}
Muhammad Asyraf
fonte
Com todo o respeito, esta é uma solução horrível. Ele só quer verificar se a UITextFieldatualizou seu valor - por que criar uma classe muito complexa para resolver esse problema simples?
Guilherme Matuella
@GuilhermeMatuella, este é mais um código de front-end para que o usuário saiba se o campo é obrigatório e preenchido. é uma abordagem diferente para resolver o mesmo problema. este é basicamente meus ativos personalizados
Muhammad Asyraf