Jak sprawdzić, kiedy zmienia się UITextField?


290

Próbuję sprawdzić, kiedy zmienia się pole tekstowe, podobnie jak funkcja użyta dla textView - do textViewDidChangetej pory zrobiłem to:

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

Który rodzaj działa, ale topRightButtonwłącza się, gdy tylko pole tekstowe zostanie wciśnięte. Chcę, aby było włączone tylko wtedy, gdy tekst jest wpisywany?

Odpowiedzi:


739

SZYBKI

Szybki 4.2

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

i

@objc func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 3 i szybki 4.1

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

i

func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 2.2

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

i

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

CEL C

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

a metoda textFieldDidChange to

-(void)textFieldDidChange :(UITextField *) textField{
    //your code
}

To się zawiesiło i nie rozumiem dlaczego.
Levi Roberts,

1
Sprawdzane wiele razy. Delegat jest ustawiany bezpośrednio przed nim w środku viewDidLoad. Akcja jest taka sama jak litera za literę. Aplikacja ulega awarii po naciśnięciu przycisku klawiatury. Edycja: zrozumiałem! Brakowało średnika w akcji. Zakładałem, że musi to być tylko nazwa funkcji.
Levi Roberts,

@FadadMasud to nic nie robi teraz w Swift 2.0 na iOS 9 z XCode 7. Czy to jest amortyzowane, czy znasz obecny sposób, aby to naprawić?
Cody Weaver,

1
@bibscy tak, musisz przewijać wszystkie pola tekstowe w widoku.
Fawad Masud

1
Dla Swift 4.2 jego: Texttfield.addTarget (self, action: #selector (ViewControllerr.textFieldDidChange (_ :)), dla: UIControl.Event.editingChanged)
Wyjdź

128

Możesz wykonać to połączenie w kreatorze interfejsów.

  1. W swoim scenorysie kliknij asystenta edytora u góry ekranu (dwa kółka na środku). Wybrano asystenta redaktora

  2. Ctrl + kliknij pole tekstowe w narzędziu do tworzenia interfejsów.

  3. Przeciągnij z EditingChanged do wewnątrz klasy kontrolera widoku w widoku asystenta. Nawiązywanie połączenia

  4. Nazwij swoją funkcję (na przykład „textDidChange”) i kliknij Połącz. Funkcja nazywania


3
Jest to świetne rozwiązanie, szczególnie w przypadku obsługi pola UITextField w tableViewCell zarządzanym przez oddzielne źródło danych. Takie podejście pozwala viewControllerowi na bezpośrednią odpowiedź (dlatego źródło danych nie musi odpowiadać i delegować akcji).
wuf810,

1
Świetnie - proste rozwiązanie irytującego problemu. Możesz oczywiście połączyć wiele pól tekstowych
Jeremy Andrews

1
Prawdopodobnie lepsza odpowiedź niż powyżej, ponieważ eliminacja dodawania @objc func.
Matthew Bradshaw,

Dobry pomysł, używam zdarzenia DidEndEditing
Puji Wahono

To najlepsze rozwiązanie. Dzięki @rmooney!
Jonathan

63

Swift 5.0

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

i sposób obsługi:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 4.0

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

i sposób obsługi:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 3.0

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

i sposób obsługi:

func textFieldDidChange(textField: UITextField) { 

}

29

Sposób, w jaki do tej pory sobie z tym poradziłem: w 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
}

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

1
Nie zostanie to wywołane, gdy pole tekstowe będzie puste, a użytkownik kliknie backspace.
Matthew Mitchell,

14

Szybki 3

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

7

Swift 3.0.1+ (niektóre inne odpowiedzi dla Swift 3.0 są nieaktualne)

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

func textFieldDidChange(_ textField: UITextField) {

}

6

textField (_: shouldChangeCharactersIn: replaceString :) działał dla mnie w Xcode 8, Swift 3, jeśli chcesz sprawdzić każde naciśnięcie klawisza.

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
}

5

Szybki 4

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

4

Możesz użyć tej metody delegowania z UITextFieldDelegate. Wystrzeliwuje przy każdej zmianie postaci.

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

TYLKO JEDYNIE OBEJMUJE PRZED dokonaniem zmiany (w rzeczywistości zmiana zostanie wprowadzona tylko wtedy, gdy zwrócisz wartość true tutaj).


1
Jak to napisać, ponieważ próbowałem również tej metody i doszedłem do tego samego rozwiązania, w którym zmienia się ona tylko po aktywacji textField, a nie kiedy tekst faktycznie się zmienia?

Po wdrożeniu powyższej metody delegowania uruchamia się ona przy każdej zmianie tekstu. Musisz tylko dodać ten kod, self.textfield.delegate = self
Abubakr Dar

Dla mnie ta metoda nie działała, ponieważ nie można było sprawdzić, czy pole tekstowe jest puste w środku metody. Przede wszystkim dlatego, że zwraca true / false w zależności od tego, czy pole tekstowe może się zmienić. Dlatego zdarzenie jest uruchamiane PRZED polem tekstowym, które miało szansę stać się puste.
Levi Roberts,

@LeviRoberts, w tej metodzie masz odniesienie do pola tekstowego. Możesz więc sprawdzić, czy textfield.text jest pusty.
Abubakr Dar

Wydaje się, że nie rozumiesz. Gdy jest pusta, .isEmptymetoda nie równa się true, dopóki PO tej metodzie nie będzie szansa na zwrócenie true; aby powiedzieć aplikacji, że pole tekstowe powinno się zmienić.
Levi Roberts,

3

Może użyć RxSwift?

potrzeba

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

oczywiście dodaj import

import RxSwift
import RxCocoa

Więc masz textfield : UITextField

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

Masz inne 3 metody.

  1. onError
  2. onCompleted
  3. onDisposed
  4. onNext

Aby otrzymywać zdarzenia o prawdziwej zmianie, a nie tylko wtedy, gdy pole tekstowe stało się pierwszą odpowiedzią, musisz użyć odrębnegoUntilChanged na tekście.
RealNmae,

1

Szybki 4

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

@objc func textIsChanging(_ textField:UITextField) {

 print ("TextField is changing")

}

Jeśli chcesz dokonać zmiany po całkowitym wpisaniu przez użytkownika (Zostanie on wywołany, gdy użytkownik zwolni klawiaturę lub naciśnij klawisz Enter).

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

 @objc func textDidChange(_ textField:UITextField) {

       print ("TextField did changed") 
 }

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

1

Powinieneś wykonać następujące kroki:

  1. Odwołaj Outlet do pola tekstowego
  2. PrzypiszUITextFieldDelegate do klasy kontrolera
  3. Skonfiguruj yourTextField.delegate
  4. Zaimplementuj dowolną potrzebną funkcję

Przykładowy kod:

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
    }

    .
    .
    .
}

0

W ten sposób można dodać textField text change listenerza pomocą Swift 3 :

Zadeklaruj swoją klasę jako UITextFieldDelegate

override func viewDidLoad() {
    super.viewDidLoad()

    textField.delegate = self

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

Następnie tradycyjnie dodaj funkcję textFieldShouldEndEditing:

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

0

Szybki 4.2

napisz to w viewDidLoad

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

napisz to poza viewDidLoad

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

Możesz zmienić zdarzenie przez UIControl.Event.editingDidBegin lub cokolwiek, co chcesz wykryć.


0

Na wypadek, gdybyś był zainteresowany rozwiązaniem SwiftUI, działa ono dla mnie:

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

Muszę powiedzieć, że to nie jest mój pomysł, przeczytałem go na tym blogu: Wiązanie SwiftUI: bardzo prosta sztuczka


0

W przypadku, gdy nie jest możliwe powiązanie addTarget z twoim UITextField, radzę ci powiązać jeden z nich, jak zasugerowano powyżej, i wstawić kod do wykonania na końcu metody 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
    }
}

I na wezwanie powinnyChangeCharacters In 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
}

-1

szybki 4

W 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

Dodaj tę funkcję:

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

-1

utwórz nową niestandardową klasę 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
}

}

Z całym szacunkiem jest to okropne rozwiązanie. Chce tylko sprawdzić, czy a UITextFieldzaktualizował swoją wartość - po co tworzyć zbyt złożoną klasę, aby rozwiązać ten prosty problem?
Guilherme Matuella

@ GuilhermeMatuella jest to więcej do kodu interfejsu użytkownika, aby użytkownik wiedział, czy pole jest wymagane i wypełnione. jest to inne podejście do rozwiązania tego samego problemu. to w zasadzie moje niestandardowe zasoby
Muhammad Asyraf
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.