Ich habe Probleme, KVO mit Textfeldern zu arbeiten, die in einer Cocoa-App miteinander verbunden sind. Ich habe das zum Funktionieren gebracht, wenn ich Zeichenketten in NSTextFields mit Knöpfen einstelle, aber es arbeitet nicht mit Bindungen. Wie immer wird jede Hilfe von Stack Overflow sehr geschätzt.
Zweck meines Codes ist:
binden mehrere Textfelder zusammen
Wenn eine Zahl in ein Feld eingegeben wird, müssen die anderen Felder automatisch aktualisiert werden
Beobachten Sie die Änderungen in den Textfeldern
Hier ist mein Code für MainClass, der eine NSObject-Unterklasse ist:
%Vor%Und hier ist die Bindung für eines der Textfelder:
In Ihrer -awakeFromNib
-Methode haben Sie
Dies geht nicht so, wie Sie es sich erhoffen: self.fieldA
ist nicht key-value Codierung kompatibel für den Schlüssel numA
: wenn Sie versuchen, -valueForKey:
oder% co_de zu senden % mit dem Schlüssel -setValue:forKey:
bis @"numA"
, erhalten Sie die folgenden Ausnahmen:
[valueForUndefinedKey:]: Diese Klasse ist für den Schlüssel numA nicht schlüsselcodefähig.
und
[setValue: forUndefinedKey:]: Diese Klasse ist für den Schlüssel numA nicht schlüsselcodefähig.
Als Ergebnis , die self.fieldA
Instanzen sind nicht Schlüsselwert beobachtend kompatibel für NSTextField
, entweder: Die erste Anforderung, die für einen Schlüssel KVO-konform sein muss, ist KVC-konform für diesen Schlüssel.
Es ist jedoch KVO-konform unter anderem für @"numA"
. Dies ermöglicht es Ihnen, was ich bereits beschrieben .
Hinweis : Dies ändert sich nicht durch die Art und Weise, wie Sie Bindungen in Interface Builder eingerichtet haben. Mehr dazu später.
Das Beobachten eines stringValue
Wertes für NSTextField
funktioniert, wenn @"stringValue"
für -setStringValue:
aufgerufen wird. Dies ist ein Ergebnis der Interna von KVO.
Wenn Sie beginnen, einen Schlüsselwert zu beobachten, der ein Objekt zum ersten Mal beobachtet, wird die Klasse des Objekts geändert - sein NSTextField
-Zeiger wird geändert. Sie können dies beobachten, indem Sie isa
Im Allgemeinen ändert sich der Name der Klasse von -addObserver:forKeyPath:options:context:
in Object
.
Wenn wir NSKVONotifying_Object
für eine Instanz von -addObserver:forKeyPath:options:context:
mit dem Schlüsselpfad Object
aufgerufen hätten - ein Schlüssel, für den Instanzen von @"property"
KVC-konform sind - rufen wir als nächstes Object
auf Unsere Instanz von -setProperty:
(tatsächlich eine Instanz von Object
), die folgenden Nachrichten werden an das Objekt
NSKVONotifying_Object
passing -willChangeValueForKey:
@"property"
passing -setProperty:
@"property"
passing -didChangeValueForKey:
Der Bruch innerhalb einer dieser Methoden zeigt, dass sie von der undokumentierten Funktion @"property"
aufgerufen werden.
Die Relevanz all dessen ist, dass die Methode _NSSetObjectValueAndNotify
für den Beobachter aufgerufen wird, den wir unserer Instanz -observeValueForKeyPath:ofObject:change:context:
für den Schlüsselpfad Object
von @"property"
hinzugefügt haben. Hier ist der Anfang des Stack-Trace:
-didChangeValueForKey:
und NSTextField
? In Ihrem vorherigen Setup waren Sie Hinzufügen eines Beobachters zu Ihrem Textfeld in @"stringValue"
. Das bedeutet, dass Ihr Textfeld bereits eine Instanz von -awakeFromNib
war.
Sie würden dann die eine oder andere Taste drücken, die wiederum NSKVONotifying_NSTextField
in Ihrem Textfeld aufrufen würde. Sie konnten diese Änderung beobachten, weil - als eine Instanz von -setStringValue
- Ihr Textfeld beim Empfang von NSKVONotifying_NSTextField
tatsächlich
setStringValue:value
willChangeValueForKey:@"stringValue"
setStringValue:value
Wie oben wird innerhalb von didChangeValueForKey:@"stringValue"
allen Objekten, die den Wert des Textfelds für didChangeValueForKey:@"stringValue"
beobachten, mitgeteilt, dass sich der Wert für diesen Schlüssel in ihren eigenen Implementierungen von @"stringValue"
geändert hat. Dies gilt insbesondere für das Objekt, das Sie als Beobachter für das Textfeld in -observeValueForKeyPath:ofObject:change:context:
hinzugefügt haben.
Zusammenfassend konnten Sie die Änderung des Textfeldwerts für -awakeFromNib
beobachten, weil Sie sich selbst als Beobachter des Textfelds für diesen Schlüssel hinzugefügt haben, und , weil @"stringValue"
für das Textfeld aufgerufen wurde .
Bisher unter dem Deckmantel der Diskussion "Das Problem mit der Schlüsselwertbeobachtung auf NSTextFields" haben wir nur den ersten Satz sinnvoll gemacht
Das Beobachten eines
-setStringValue
Wertes fürNSTextField
funktioniert, wenn@"stringValue"
für-setStringValue:
aufgerufen wird.
Und das klingt großartig! Also, was ist das Problem?
Das Problem ist, dass NSTextField
nicht auf dem Textfeld aufgerufen wird, während der Benutzer es eingibt oder auch nachdem der Benutzer die Bearbeitung beendet hat (z. B. durch Ausblenden aus dem Textfeld). (Außerdem werden -setStringValue:
und -willChangeValueForKey:
nicht manuell aufgerufen. Wenn dies der Fall wäre, würde unsere KVO funktionieren; tut es aber nicht.) Dies bedeutet, dass unsere KVO auf -didChangeValueForKey:
funktioniert, wenn @"stringValue"
auf der Im Textfeld funktioniert NICHT , wenn der Benutzer selbst Text eingibt.
TL; DR : KVO auf -setStringValue:
von @"stringValue"
ist nicht gut genug, da es nicht für Benutzereingaben funktioniert.
Versuchen wir es mit Bindings.
Erstellen Sie ein Beispielprojekt mit einem separaten Fenstercontroller (ich habe den Creative-Namen NSTextField
verwendet) mit XIB. ( Hier ist das Projekt, bei dem ich auf GitHub beginne. ) In WindowController
hat eine Eigenschaft WindowController.m
in einer Klassenerweiterung hinzugefügt:
Erstellen Sie in Interface Builder ein Textfeld und öffnen Sie den Bindungsinspektor:
Erweitern Sie unter der Überschrift "Wert" den Wert "Wert":
Die Popup-Schaltfläche neben dem Kontrollkästchen "Bind to" (Bindet an) hat derzeit "Shared User Defaults Controller" ausgewählt. Wir möchten den Wert des Textfelds an unsere stringA
-Instanz binden. Wählen Sie stattdessen "Dateibesitzer". Wenn dies geschieht, wird das Feld "Controller Key" geleert und das Feld "Model Key Path" wird in "self" geändert.
Wir möchten den Wert dieses Textfelds an die WindowController
-Instanzeneigenschaft WindowController
binden, also den "Modellschlüsselpfad" in stringA
:
An diesem Punkt sind wir fertig. ( Bisheriger Fortschritt bei GitHub. ) Wir haben den Wert des Textfelds erfolgreich an die Eigenschaft self.stringA
gebunden %Code%.
Wenn wir WindowController
auf einen Wert in -init setzen, wird dieser Wert beim Laden des Fensters im Textfeld angezeigt:
Und schon haben wir Bindungen in die andere Richtung eingerichtet; Nach Beendigung der Bearbeitung im Textfeld wird die Eigenschaft stringA
des Fenstercontrollers gesetzt. Wir können dies überprüfen, indem wir den Setter überschreiben:
Nachdem Sie einen Text in das Textfeld eingegeben und die Tabulatortaste gedrückt haben, sehen wir, dass
ausgedruckt wird %Vor%Das sieht gut aus. Warum haben wir die ganze Zeit nicht darüber gesprochen ??? Es ist ein bisschen ein Problem hier: die lästige Drücken Tab Ding. Wenn Sie den Wert eines Textfelds an eine Zeichenfolge binden, wird der Zeichenfolgenwert erst festgelegt, wenn die Bearbeitung im Textfeld beendet ist.
Aber es gibt noch Hoffnung! Die Kakao-Bindungs-Dokumentation für stringA
gibt diesen an Bindeoption für stringA
ist NSTextField
. Und siehe da, es gibt ein Kontrollkästchen, das genau dieser Option im Bindungsinspektor für NSTextFields Wert entspricht. Gehen Sie weiter und überprüfen Sie diese Box.
Mit dieser Änderung, während wir Dinge eingeben, wird die Aktualisierung der Eigenschaft NSTextField
des Fenster-Controllers fortlaufend ausgeloggt:
Schließlich aktualisieren wir fortlaufend die Zeichenfolge des Fenstercontrollers aus dem Textfeld. Der Rest ist einfach. Fügen Sie dem Fenster ein paar weitere Textfelder hinzu, binden Sie sie an stringA und legen Sie fest, dass sie fortlaufend aktualisiert werden. Sie haben zu diesem Zeitpunkt drei synchronisierte NSContinuouslyUpdatesValueBindingOption
s! Hier ist das Projekt mit drei synchronisierten Textfeldern.
Sie möchten drei Textfelder einrichten, die Zahlen anzeigen, die zueinander in Beziehung stehen. Da wir uns jetzt mit Zahlen beschäftigen, entfernen wir die Eigenschaft stringA
von NSTextField
und ersetzen sie durch stringA
, WindowController
und numberA
:
Als Nächstes binden wir das erste Textfeld an die NummerA des Eigentümers der Datei, die zweite an NummerB und so weiter. Schließlich müssen wir nur eine Eigenschaft hinzufügen, die die Menge ist, die auf diese verschiedenen Arten dargestellt wird. Nennen wir diesen Wert numberB
.
Wir brauchen die konstanten Umrechnungsfaktoren, um von den Einheiten von numberC
in die Einheiten quantity
usw. zu transformieren, also fügen Sie
(Verwenden Sie natürlich die Zahlen, die für Ihre Situation relevant sind.) Damit können wir die Accessoren für jede der Zahlen implementieren:
%Vor% All die verschiedenen Access-Nummern sind jetzt nur indirekte Mechanismen für den Zugriff auf quantity
und eignen sich perfekt für Bindungen. Es gibt nur noch eine zusätzliche Sache, die noch erledigt werden muss: Wir müssen sicherstellen, dass die Beobachter alle Zahlen neu aufwerten, wenn numberA
geändert wird:
Wenn Sie nun in eines der Textfelder eingeben, werden die anderen entsprechend aktualisiert. Hier ist die endgültige Version des Projekts auf GitHub .
Tags und Links objective-c cocoa nstextfield key-value-observing cocoa-bindings