UITextField-Textänderungsereignis


563

Wie kann ich Textänderungen in einem textField erkennen? Die Delegate-Methode shouldChangeCharactersInRangefunktioniert für etwas, hat aber meine Anforderungen nicht genau erfüllt. Da die textField-Texte bis zur Rückgabe von YES anderen Beobachtermethoden nicht zur Verfügung stehen.

zB in meinem Code calculateAndUpdateTextFieldsnicht den aktualisierten Text erhalten, hat der Benutzer eingegeben.

Ist ihre Möglichkeit, so etwas zu bekommen textChanged Java Event Handler .

- (BOOL)textField:(UITextField *)textField 
            shouldChangeCharactersInRange:(NSRange)range 
            replacementString:(NSString *)string 
{
    if (textField.tag == kTextFieldTagSubtotal 
        || textField.tag == kTextFieldTagSubtotalDecimal
        || textField.tag == kTextFieldTagShipping
        || textField.tag == kTextFieldTagShippingDecimal) 
    {
        [self calculateAndUpdateTextFields];

    }

    return YES;
}

3
Dies ist ein gutes Beispiel für eine SO-Antwort mit 1000 Stimmen, die völlig falsch ist . iOS ist knifflig, suibtle und voller Gotchyas.
Fattie

Antworten:


1056

Von der richtigen Art, einen Textwechsel in Uitextfield durchzuführen, rufen Sie zurück :

Ich fange die an ein UITextField-Steuerelement gesendeten Zeichen wie folgt ab:

// Add a "textFieldDidChange" notification method to the text field control.

In Ziel-C:

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

In Swift:

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

Anschließend können Sie in der textFieldDidChangeMethode den Inhalt des Textfelds untersuchen und Ihre Tabellenansicht nach Bedarf neu laden.

Sie können das verwenden und berechneAndUpdateTextFields als Ihre setzen selector.


18
Dies ist die bessere Lösung - da Sie dies auch in Nibs oder Storyboards festlegen können und keinen übermäßigen UITextFieldTextDidChangeNotification-Code schreiben müssen
PostCodeism

3
Das einzige Problem ist. Es funktioniert nicht für mich zusammen mit - (BOOL) textField: (UITextField *) textField shouldChangeCharactersInRange: (NSRange) Bereich ErsatzString: (NSString *) Zeichenfolge
iWheelBuy

4
@iWheelBuy Nur wenn es zurückkehrt NO, was logisch ist, denn wenn Sie NOvon dieser Methode zurückkehren, sagen Sie im Grunde, dass sich der Text im Feld nicht ändern sollte.
Gitaarik

2
Dies ist ideal, um verursachte Änderungen zu bearbeiten. Programmatische Änderungen, die auftreten über : textField.text = "Some new value";. Gibt es eine clevere Möglichkeit, dies zu erfassen?
Benjohn

10
Sie hätten bei Apple gedacht, UITextFieldDelegatedass so etwas enthalten gewesen func textField: UITextField, didChangeText text: Stringwäre, aber ... (gibt Jungs bei Apple einen schmutzigen Blick)
Brandon A

394

Die Antwort von XenElement ist genau richtig.

Dies kann auch im Interface Builder durchgeführt werden, indem Sie mit der rechten Maustaste auf das UITextField klicken und das Sendeereignis "Bearbeiten geändert" auf Ihre Unterklasseneinheit ziehen.

UITextField-Änderungsereignis


2
Es mag einfach sein, aber nach 10-15 Minuten Suche hatte ich diese Möglichkeit erst entdeckt, als ich zufällig auf Ihre Antwort gestoßen bin. Habe noch +1 von mir; Es ist schön, dass sowohl die codebasierten als auch die IB-basierten Lösungen bei Fragen wie diesen hoch abgestimmt sind.
Mark Amery

Ich habe es gerade in 6.3 überprüft und es ist da. Dies ist eine UITextView und klicken Sie mit der rechten Maustaste darauf und sehen Sie "Bearbeiten geändert".
William T.

4
Warum um alles in der Welt heißt es Bearbeiten geändert? wütend! danke für die lösung :-)
malhal

3
Da das Ereignis beim Bearbeiten von Änderungen aufgerufen wird, während Value Changed auftritt, wenn das Textfeld die Bearbeitung beendet hat, dh wenn der Ersthelfer zurücktritt. UITextFieldTextDidChangeNotification ist eine UITextField-Benachrichtigung, während UIControlEventValueChanged von UIControl implementiert wird, dessen UIButton-Unterklassen.
Camsoft

2
Ich stelle fest, dass dies nicht funktioniert, wenn Benutzerregisterkarten aus dem Textfeld entfernt werden und ein Autokorrekturtext den Inhalt ersetzt
noobular

136

So stellen Sie den Ereignis-Listener ein:

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

um tatsächlich zuzuhören:

- (void)textFieldDidChange:(UITextField *)textField {
    NSLog(@"text changed: %@", textField.text);
}

cool. Ich mag diesen Ansatz, weil ich textField-change-listen in einer ViewController-Basisklasse implementieren möchte, die bereits TextFieldDelegate ist. Auf diese Weise kann ich Target: baseControllerMethod for (textField in Unteransichten) wie einen Zauber hinzufügen. DANKE!
HBublitz

87

Schnell:

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

Implementieren Sie dann die Rückruffunktion:

@objc func textFieldDidChange(textField: UITextField){

print("Text changed")

}

2
Arbeitete für mich, ich hatte dies bereits versucht, aber nicht erkannt, dass die Funktion ein textField-Argument benötigt. Wenn dies bei Ihnen nicht funktioniert, stellen Sie sicher, dass Ihr Selektor ein Argument für die Übergabe des Textfelds enthält (auch wenn Sie es nicht verwenden).
werm098

Fügen Sie _ vor textField in textFieldDidChange wie folgt hinzu: func textFieldDidChange (textField: UITextField) {...}
Makalele

30

Wie hier angegeben: UITextField-Textänderungsereignis Es scheint, dass es ab iOS 6 (iOS 6.0 und 6.1 aktiviert) nicht möglich ist, Änderungen an UITextFieldObjekten vollständig zu erkennen, wenn Sie nur das beobachten UITextFieldTextDidChangeNotification.

Es scheint, dass jetzt nur die Änderungen verfolgt werden, die direkt über die integrierte iOS-Tastatur vorgenommen wurden. Dies bedeutet, dass Sie nicht über Änderungen informiert werden , wenn Sie Ihr UITextFieldObjekt nur durch Aufrufen von Folgendes ändern : myUITextField.text = @"any_text".

Ich weiß nicht, ob dies ein Fehler ist oder beabsichtigt ist. Scheint ein Fehler zu sein, da ich in der Dokumentation keine vernünftige Erklärung gefunden habe. Dies wird auch hier angegeben: UITextField-Textänderungsereignis .

Meine "Lösung" besteht darin, für jede Änderung, die ich an meiner vornehme, eine Benachrichtigung von mir selbst zu veröffentlichen UITextField(wenn diese Änderung ohne Verwendung der integrierten iOS-Tastatur vorgenommen wird). Etwas wie das:

myUITextField.text = @"I'm_updating_my_UITextField_directly_in_code";

NSNotification *myTextFieldUpdateNotification  = 
  [NSNotification notificationWithName:UITextFieldTextDidChangeNotification
                  object:myUITextField];

[NSNotificationCenter.defaultCenter 
  postNotification:myTextFieldUpdateNotification];

Auf diese Weise können Sie zu 100% sicher sein, dass Sie dieselbe Benachrichtigung erhalten, wenn Sie die .textEigenschaft Ihres UITextFieldObjekts ändern , entweder wenn Sie es "manuell" in Ihrem Code aktualisieren oder über die integrierte iOS-Tastatur.

Da dies kein dokumentiertes Verhalten ist, kann dieser Ansatz dazu führen, dass zwei Benachrichtigungen für dieselbe Änderung in Ihrem UITextFieldObjekt empfangen werden. Abhängig von Ihren Anforderungen (was Sie tatsächlich tun, wenn Sie UITextField.textÄnderungen vornehmen) kann dies eine Unannehmlichkeit für Sie sein.

Ein etwas anderer Ansatz wäre das Posten einer benutzerdefinierten Benachrichtigung (dh mit einem anderen benutzerdefinierten Namen als UITextFieldTextDidChangeNotification), wenn Sie tatsächlich wissen müssen, ob die Benachrichtigung Ihre oder "iOS-made" war.

BEARBEITEN:

Ich habe gerade einen anderen Ansatz gefunden, der meiner Meinung nach besser sein könnte:

Dies beinhaltet die Key-Value Observing (KVO) -Funktion von Objective-C ( http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid) / 10000177-BCICJDHA ).

Grundsätzlich registrieren Sie sich als Beobachter einer Immobilie und wenn sich diese Eigenschaft ändert, werden Sie darüber informiert. Das "Prinzip" ist der Funktionsweise ziemlich ähnlich. NSNotificationCenterDies ist der Hauptvorteil, dass dieser Ansatz auch ab iOS 6 automatisch funktioniert (ohne spezielle Anpassungen wie das manuelle Posten von Benachrichtigungen).

In unserem UITextFieldSzenario funktioniert dies einwandfrei, wenn Sie diesen Code beispielsweise zu Ihrem Code hinzufügen UIViewController, der das Textfeld enthält:

static void *myContext = &myContext;

- (void)viewDidLoad {
  [super viewDidLoad];

  //Observing changes to myUITextField.text:
  [myUITextField addObserver:self forKeyPath:@"text"
    options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 
    context:myContext];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
change:(NSDictionary *)change context:(void *)context {

  if(context == myContext) {
    //Here you get notified every time myUITextField's "text" property is updated
    NSLog(@"New value: %@ - Old value: %@",
      [change objectForKey:NSKeyValueChangeNewKey],
      [change objectForKey:NSKeyValueChangeOldKey]);
  }
  else 
    [super observeValueForKeyPath:keyPath ofObject:object 
      change:change context:context];

}

Gutschrift auf diese Antwort bezüglich "Kontext" -Verwaltung: https://stackoverflow.com/a/12097161/2078512

Hinweis: Scheint , wie während Sie sind in dem Prozess der eine Bearbeitung UITextFieldmit dem eingebauten in iOS - Tastatur, die „Text“ Eigenschaft des Textfeldes wird nicht mit jedem neuen Buchstaben aktualisiert getippt / entfernt. Stattdessen wird das Textfeldobjekt "als Ganzes" aktualisiert, nachdem Sie den Ersthelferstatus des Textfelds aufgegeben haben.


5
Warum sollten Sie dies tun? Verwenden Sie einfach die Zielaktionsmethode aus XenElements Antwort. Wenn Sie Dutzende von Codezeilen benötigen, tun Sie etwas, das "einfach" sein sollte, und machen es wahrscheinlich falsch.
Jrturton

6
Das scheint beabsichtigtes Verhalten zu sein. Für Ereignisse, die vom Code stammen, werden keine anderen Delegatmethoden (z. B. Scrollen, Auswahl) aufgerufen.
Jrturton

1
Beachten Sie, dass UIKit nicht garantiert KVO-konform ist , sodass die KVO-Lösung nicht unbedingt funktioniert.
Pang

@jrturton, in Bezug auf Ihre "Warum" -Frage ist es völlig üblich, dass Sie in einer anderen Klasse (vielleicht einer Dekoration, Animation oder dergleichen) auf das reagieren müssen, was im Textfeld passiert.
Fattie

27

Wir können das einfach konfigurieren, indem Sie bei Storyboardgedrückter STRG-Taste das @IBActionEreignis ändern und wie folgt ändern:

Geben Sie hier die Bildbeschreibung ein


14

Hier in schneller Version für das gleiche.

textField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)

func textFieldDidChange(textField: UITextField) {

}

Vielen Dank


12

Ich habe das Problem behoben, durch das das Verhalten von shouldChangeChractersInRange geändert wurde. Wenn Sie NEIN zurückgeben, werden die Änderungen von iOS nicht intern übernommen. Stattdessen haben Sie die Möglichkeit, sie manuell zu ändern und nach den Änderungen alle Aktionen auszuführen.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    //Replace the string manually in the textbox
    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
    //perform any logic here now that you are sure the textbox text has changed
    [self didChangeTextInTextField:textField];
    return NO; //this make iOS not to perform any action
}

3
Das Problem bei dieser Lösung ist: Die Cursorposition wird auf das ENDE des Feldes gesetzt (sobald Sie textField.text = ... ausführen). Wenn der Benutzer also Zeichen in der Mitte des Feldes bearbeiten möchte, bewegt sich der Cursor bei jedem Tastendruck zum Ende des Feldes. Probieren Sie es aus und sehen Sie.
FranticRock

Guter Punkt @Alex, aber einfach zu umgehen. Berechnen Sie einfach den resultierenden Wert in einem lokalen NSString und übergeben Sie ihn an Ihre didChangeTextInTextField: toText: -Methode. Geben Sie dann YES zurück, damit iOS das Update durchführen kann.
jk7

11

Swift-Version getestet:

//Somewhere in your UIViewController, like viewDidLoad(){ ... }
self.textField.addTarget(
        self, 
        action: #selector(SearchViewController.textFieldDidChange(_:)),
        forControlEvents: UIControlEvents.EditingChanged
)

Parameter erklärt:

self.textField //-> A UITextField defined somewhere in your UIViewController
self //-> UIViewController
.textFieldDidChange(_:) //-> Can be named anyway you like, as long as it is defined in your UIViewController

Fügen Sie dann die oben erstellte Methode in Folgendes ein UIViewController:

//Gets called everytime the text changes in the textfield.
func textFieldDidChange(textField: UITextField){

    print("Text changed: " + textField.text!)

}

7

Swift 4

func addNotificationObservers() {

    NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChangeAction(_:)), name: .UITextFieldTextDidChange, object: nil)

}

@objc func textFieldDidChangeAction(_ notification: NSNotification) {

    let textField = notification.object as! UITextField
    print(textField.text!)

}

5

Für Swift 3.0:

let textField = UITextField()

textField.addTarget(
    nil,
    action: #selector(MyClass.textChanged(_:)),
    for: UIControlEvents.editingChanged
)

mit Klasse wie:

class MyClass {
    func textChanged(sender: Any!) {

    }
}

4

Swift 3 Version

yourTextField.addTarget(self, action: #selector(YourControllerName.textChanges(_:)), for: UIControlEvents.editingChanged)

Und holen Sie sich die Änderungen hier

func textChanges(_ textField: UITextField) {
    let text = textField.text! // your desired text here
    // Now do whatever you want.
}

Ich hoffe es hilft.


2

Sie sollten die Benachrichtigung verwenden, um dieses Problem zu lösen, da die andere Methode das Eingabefeld und nicht die tatsächlich eingegebene Methode abhört, insbesondere wenn Sie die chinesische Eingabemethode verwenden. In viewDidload

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFiledEditChanged:)
                                                 name:@"UITextFieldTextDidChangeNotification"
                                               object:youTarget];

dann

- (void)textFiledEditChanged:(NSNotification *)obj {
UITextField *textField = (UITextField *)obj.object;
NSString *toBestring = textField.text;
NSArray *currentar = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [currentar firstObject];
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
    UITextRange *selectedRange = [textField markedTextRange];
    UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
    if (!position) {
        if (toBestring.length > kMaxLength)
            textField.text =  toBestring;
} 

}}

Endlich rennst du, fertig.


2

Swift 3.1:

Auswahl: ClassName.MethodName

  cell.lblItem.addTarget(self, action: #selector(NewListScreen.fieldChanged(textfieldChange:)), for: .editingChanged)

  func fieldChanged(textfieldChange: UITextField){

    }

2

Swift 3 Version:

class SomeClass: UIViewController, UITextFieldDelegate { 

   @IBOutlet weak var aTextField: UITextField!


    override func viewDidLoad() {
        super.viewDidLoad()

        aTextField.delegate = self
        aTextField.addTarget(self, action: #selector(SignUpVC.textFieldDidChange), for: UIControlEvents.editingChanged)        
    }

   func textFieldDidChange(_ textField: UITextField) {

       //TEXT FIELD CHANGED..SECRET STUFF

   }

}

Vergessen Sie nicht, den Delegaten festzulegen.


Ich habe 6 Textfelder in meinem Ansichts-Controller und möchte nur überprüfen, ob das letzte Textfeld geändert wurde. Drucken ("Das letzte Textfeld wurde geändert!") Können Sie helfen?
Saeed Rahmatolahi

Machen Sie zuerst Iboutlet für Ihr Textfeld. Stellen Sie dann den Delegaten und das AddTarget so ein, wie ich es in meiner Antwort gezeigt habe. Fügen Sie in der Delegatfunktion textFieldDidChange Folgendes hinzu: "if textField == yourLastTextField {print (" Das letzte Textfeld wurde geändert! ")} Im Moment habe ich keinen Zugriff auf meinen Computer. Ich werde diesen Kommentar lesbarer machen, wenn ich Geh zu meinem Computer.
Joseph Francis

2

Mit Verschluss:

   class TextFieldWithClosure: UITextField {
    var targetAction: (() -> Void)? {
        didSet {
            self.addTarget(self, action: #selector(self.targetSelector), for: .editingChanged)
        }
    }

    func targetSelector() {
        self.targetAction?()
    }
    }

und mit

textField.targetAction? = {
 // will fire on text changed
 }

2
  1. KVO-Lösungen funktionieren nicht

KVO funktioniert unter iOS NICHT für Steuerelemente: http://stackoverflow.com/a/6352525/1402846 https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KVO.html

  1. In der Beobachtungsklasse machen Sie Folgendes:

Vorausgesetzt, Sie kennen die Textansicht, die Sie ansehen möchten:

var watchedTextView: UITextView!

Mach das:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(changed),
    name: UITextView.textDidChangeNotification,
    object: watchedTextView)

Seien Sie jedoch vorsichtig damit:

  • Es ist wahrscheinlich, dass Sie das nur einmal aufrufen möchten. Rufen Sie es also nicht an, z.layoutSubviews

  • Es ist ziemlich schwierig zu wissen, wann Sie es während Ihres Aufrufprozesses am besten anrufen können. Es wird von Ihrer Situation abhängen. Leider gibt es keine Standardlösung

  • Zum Beispiel kann man es normalerweise sicher nicht rechtzeitig aufrufen init, da es natürlich noch watchedTextViewnicht existiert

.

  1. Das nächste Problem ist, dass ...

Keine der Benachrichtigungen wird aufgerufen, wenn der Text programmgesteuert geändert wird .

Dies ist ein riesiges, uraltes und dummes Ärgernis in der iOS-Technik.

Steuerelemente rufen die Benachrichtigungen einfach nicht auf, wenn die Eigenschaft .text programmgesteuert geändert wird.

Dies ist wahnsinnig ärgerlich, da natürlich - offensichtlich - jede App, die jemals erstellt wurde, den Text programmgesteuert festlegt, z. B. das Löschen des Felds nach dem Posten durch den Benutzer usw.

Sie müssen die Textansicht (oder ein ähnliches Steuerelement) wie folgt unterordnen:

class NonIdioticTextView: UIITextView {

    override var text: String! {
        // boilerplate code needed to make watchers work properly:
        get {
            return super.text
        }
        set {
            super.text = newValue
            NotificationCenter.default.post(
                name: UITextView.textDidChangeNotification,
                object: self)
        }

    }

}

(Tipp - nicht das Super Anruf vergessen hat zu kommen , bevor die Post an!) .

Es ist keine Lösung verfügbar, es sei denn, Sie korrigieren das Steuerelement durch Unterklassen wie oben gezeigt. Das ist die einzige Lösung.

Beachten Sie, dass die Benachrichtigung

UITextView.textDidChangeNotification

führt zu

func textViewDidChangeSelection(_ textView: UITextView) 

gerufen werden.

( Nicht textViewDidChange .)


1
[[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(didChangeTextViewText:) 
name:UITextFieldTextDidChangeNotification object:nil];



- (void) didChangeTextViewText {
 //do something
}

0

Eine Sache ist, dass Sie möglicherweise mehrere UITextFields haben. Geben Sie ihnen ein Tag und Sie können die Tags einschalten. Hier erfahren Sie, wie Sie einen Beobachter in einer beliebigen Klasse einrichten.

private func setupTextFieldNotification() {
    NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in
        if let textField = notification.object as? UITextField, textField.tag == 100, let text = textField.text {
            print(#line, text)
        }
    }
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

-1

Swift 4 Version

Verwenden der Schlüsselwertbeobachtung Benachrichtigen Sie Objekte über Änderungen an den Eigenschaften anderer Objekte.

var textFieldObserver: NSKeyValueObservation?

textFieldObserver = yourTextField.observe(\.text, options: [.new, .old]) { [weak self] (object, changeValue) in
  guard let strongSelf = self else { return }
  print(changeValue)
}

Gute Idee. Können Sie dies in einen Kontext einwickeln und uns Bescheid geben?
Revanth Kausikan

1
Das ist leider völlig falsch. KVO funktioniert unter iOS NICHT für Steuerelemente: stackoverflow.com/a/6352525/1402846 developer.apple.com/library/archive/documentation/General/…
Fattie
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.