Programmieren mit Swift - Für macOS und iOS
Programmieren mit Swift - Für macOS und iOS
NSObjectController, Teil 2

Ein Großteil des Binding haben Sie jetzt schon erledig. Werden für den Character neue Werte ausgewürfelt, werden diese in dem Objekt rpgCharacer gespeichert und die grafische Oberfläche automatisch aktualisiert. Genau anders herum verhält es sich mit dem Spielfigurnamen. Dieser wird vom Benutzer eingegeben und muss jetzt von dem View in das Model, also von der grafischen Oberfläche in das rpgCharacter-Objekt.

Auch dieses Steuerelement wird wie die Label zuvor auf die gleiche Art mit dem Character Controller verbunden. Dabei treffen Sie aber auf ein Problem, das schon auftrat, als Sie das Binding noch im Code erstellten: Das Model wird erst aktualisiert, wenn man die Eingaben für den Spielfigurnamen mit Tab oder Enter bestätigt. Mit einer kleinen Änderung an der testBinding-Methode können Sie das selbst kontrollieren.
- (IBAction)testBinding:(id)sender
{
    NSLog(
@"%@", [rpgCharacter characterName] );
    NSLog(
@"%@", [rpgCharacter strength] );
}
stacks_image_0AD0848B-0509-4E37-89AD-CCD948874117
Obwohl im Textfeld ein Name eingetragen ist, wird er im Konsolenfenster nicht ausgegeben, wenn Sie auf den Button Testen klicken. Die Eingabe muss erst bestätigt werden. Diese Verhalten mag auf den ersten Blick logisch klingen, kann aber auch zu Problemen führen. Stellen Sie sich vor, ein Benutzer würde nach einer Eingabe in einer Anwendung seine Daten speichern wollen, ohne zuvor seine Eingaben bestätigt zu haben. In gutem Glauben, dass alles, was auf dem Bildschirm zu sehen ist, auch in der Datei archiviert wird, ist hier Datenverlust vorprogrammiert.

Es gibt verschiedene Möglichkeiten, mit diesem Problem umzugehen. Die einfachste ist, das angebundene Model nach jedem Tastendruck zu aktualisieren. Es ist wirklich sehr einfach, denn Sie müssen im Interface Builder nur die Checkbox Continuously Updates Value für das Textfeld aktivieren.
stacks_image_8C43A183-302D-43E0-BBA7-86ED1B9C74A3
In machen Fällen ist aber auch diese Lösung nicht optimal. Da das Model jetzt immer aktualisiert wird, ist es nicht möglich, die Eingabe abzubrechen und zum ursprünglichen Wert zurückzukehren. Besser wäre es, dem Character Controller mitzuteilen, wann er sich mit Daten aktualisieren soll. So etwas kann vor dem Speichern geschehen, oder in diesem Programm, vor dem Testen.

Um vom Programm her mit dem Character Controller zu kommunizieren, muss auch für dieses Objekt ein Outlet im Code erzeugt werden. Ebenso muss anschliessend eine Verbindung von Outlet und Steuerelement im Interface Builder gezeichnet werden.
IBOutlet NSObjectController *myCharacterController;
Vergessen Sie nicht ,die Eigenschaft Continuously Updates Value zu deaktivieren, sonst werden sie keinen Unterschied feststellen.

Um den Character Controller zu aktualisieren, genügt es, die Nachricht commitEditing an ihn zu senden. Ist die Aktualisierung erfolgreich, gibt dieser Aufruf ein YES zurück.
- (IBAction)testBinding:(id)sender
{
    if ([myCharacterController commitEditing] == YES )
    {
        NSLog(@"%@", [rpgCharacter characterName] );
        NSLog(@"%@", [rpgCharacter strength] );
    }
    else
    {
        NSLog(@"Ein Fehler ist aufgetreten.");
    }
}
In diesem Zusammenhang eine sehr wichtige Methode ist discardEditing. Durch diese Anweisung werden alle Eingaben auf der Oberfläche rückgängig gemacht, und der View wird mit den Daten aus dem Model neu aufgebaut.
[myCharacterController discardEditing];
In dieser Anwendung ist discardEditing allerdings wenig sinnvoll, da nur ein Teil der Daten wirklich von der Oberfläche kommt. Einzig der Spielfigurname ist eine Eingabe. Die Zufallszahlen werden im Code erzeugt und das Model direkt aktualisiert, ohne dass der ObjectController hier überhaupt verwendet wird. Wollte man auch das Würfeln mit Hilfe eines ObjectController rückgängig machen, müsste man erst alle Zufallszahlen auf die Oberfläche bringen und nur auf Anweisung hin in das Model aktualisieren.

Trotzdem haben Sie die Anwendung mit Hilfe eines NSObjectController komplett auf Cocoa-Binding umgestellt. Ein Menge Schritte waren nötig, um dies zu erreichen und um ein einzelnes Objekt zu binden vielleicht auch etwas viel Aufwand. Es gibt aber noch weitere Controller, die auf die gleiche Art arbeiten, statt einem Objekt aber eine ganze Liste von Objekten verwalten können. Ein NSArrayController ist ein solcher Baustein, den Sie auch noch kennen lernen werden.

nächste Seite