NSArrayController - nicht definierbares Verhalten

  • NSArrayController - nicht definierbares Verhalten

    Hallo,

    momentan schreibe ich meinen Vokabeltrainer um. Bisher habe ich die Daten/Views etc. ohne ArrayController verwaltet (alles mit dataSources gemacht). Dies möchte ich nun ändern. Dabei bin ich auf ein Problem gestoßen, welches ich nicht lösen kann.

    Ich schildere die momentane Situation und dieses nicht definierbare Verhalten. Bin mir sicher, dass ihr einige Tipps parat habt.

    Die Daten werden momentan in einer Datenbank gespeichert. Zu Beginn lade ich die Daten (z.B einige Karteikarten) in ein Model (FileCard). Dieses Model ist KVC-entsprechend. Zusätzlich habe ich einen ArrayController, welcher FileCard Objekte enthalten kann. Via Code füge ich dann die FileCard Objekte, die vom ArrayController verwaltet werden sollen wie folgt hinzu:

    Quellcode

    1. NSArray *fileCards = [[SelectedFileBox selectedFileBox] fileCards];
    2. [fileCardsController setContent:[NSMutableArray array]];
    3. int i;
    4. for(i = 0; i != [fileCards count]; i++) {
    5. [fileCardsController addObject:[fileCards objectAtIndex:i]];
    6. }


    Das funktioniert alles wunderprächtig. Nun habe ich ja allerdings zwei verschiedene Dinge... einen ArrayController mit FileCards, der diese in einer TableView anzeigt und eine Datenbank, in der die Daten gespeichert werden. Ändert der Benutzer nun einen Eintrag in der TableView, so wird diese Änderung folglich auch im ArrayController getan. Aber nicht in der Datenbank... dh ich muss die "edit"-Events irgendwie abfangen und ebenfalls in die Datenbank schreiben, damit Inhalt des ArrayController + Inhalt der Datenbank übereinstimmen, sodass der Benutzer beim nächsten Start der Applikation keinen Schreck bekommt.

    Soweit so gut...

    NSTableView hat leider keine Delegate Methode, die mich über die Art und Weise der vorgenommenen Bearbeitung einer Zeile in NSTableView informiert. Ich habe lediglich eine entsprechende Methode gefunden, die NSTableView aufruft... tap tap... Subclassen ist angesagt...

    "Abfangen" möchte ich:

    - (BOOL)textShouldEndEditing:(NSText *)textObject

    von NSTableView und als Delegate verfügbar machen. Ob das in Cocoa der richtige Weg ist - k/A...

    Aber ich habe es mal gemacht:

    Quellcode

    1. @implementation FileCardTableView
    2. - (BOOL)textShouldEndEditing:(NSText *)textObject {
    3. [self textShouldEndEditingDelegate:textObject];
    4. return YES;
    5. }
    6. - (BOOL)textShouldEndEditingDelegate:(NSText *)textObject {
    7. BOOL ok = YES;
    8. if([[self delegate] respondsToSelector:@selector(textShouldEndEditingDelegate:)]) {
    9. ok = [[self delegate] textShouldEndEditingDelegate:textObject];
    10. }
    11. return ok;
    12. }
    13. @end
    Alles anzeigen


    Nun müsste ich in meinem TableViewDelegate ja mittels "textShouldEndEditingDelegate" über Editierversuche meines gelieben Benutzers doch informiert werden - und ja - es klappt...

    In meinem TableViews Delegage habe ich folgende Funktion:

    Quellcode

    1. - (FileCard *)selectedFileCard {
    2. return [[fileCardsController selectedObjects] objectAtIndex:0];
    3. }


    Diese gibt mir einen Zeige auf die momentan selektierte Karteikarte zurück. fileCardsController ist der NSArrayController mit den FileCard Objekten.

    Ob das so ganz der richtige Weg ist - bin ich mir nicht sicher... aber seis drum...

    In meinem TableView Delegate kann ich nun mit meinem eigenen Delegate die Editierversuche abfangen und versuchen in die Datenbank zu schreiben.

    Gesagt getan.

    Quellcode

    1. - (BOOL)textShouldEndEditingDelegate:(NSText *)textObject {
    2. FileCard *fileCard = [self selectedFileCard]; // aktuell selektierte karteikeite
    3. FileCardDAO *fileCardDAO = [[FileCardDAO alloc] init]; // mein data access object
    4. FileCard *dbFileCard = [fileCardDAO loadWithFileCardId:[fileCard fileCardId]]; // kopie der aktuellen Karteikarte aus DB holen
    5. [dbFileCard setFrontSide:[textObject string]]; // neuen Wert für ein member von FileCard
    6. [fileCardDAO updateWithFileCard:dbFileCard]; // Karteikarte wieder in DB schreiben (update)
    7. [fileCard setFrontSide:[textObject string]]; // ArrayController-Element updaten
    8. return YES;
    9. }


    Und was geschieht beim editieren? Die Daten werden richtig in der Datenbank auf den neusten Stand gebracht aber in der Zelle, die bearbeitet steht nun plötzlich der Wert der Zelle über der bearbeiteten. (siehe 1. Screenshot)

    Komisch dabei ist: Falls ich eine weniger dynamische Version meines Quelltextes verwende:

    Quellcode

    1. - (BOOL)textShouldEndEditingDelegate:(NSText *)textObject {
    2. FileCard *fileCard = [self selectedFileCard]; // aktuell selektierte karteikeite
    3. FileCardDAO *fileCardDAO = [[FileCardDAO alloc] init]; // mein data access object
    4. FileCard *dbFileCard = [fileCardDAO loadWithFileCardId:[fileCard fileCardId]]; // kopie der aktuellen Karteikarte aus DB holen
    5. [dbFileCard setFrontSide:[textObject string]]; // neuen Wert für ein member von FileCard
    6. [fileCardDAO updateWithFileCard:dbFileCard]; // Karteikarte wieder in DB schreiben (update)
    7. [fileCard setFrontSide:@"FUCK"]; // ArrayController-Element updaten
    8. return YES;
    9. }


    dann klappt es wunderbar, wie erwartet (siehe 2. Screenshot) Ich habe nur eine Zeile verändert.

    [fileCard setFrontSide:[textObject string]];
    wurde zu
    [fileCard setFrontSide:@"FUCK"];

    Was habe ich übersehen?

    Der, der den Fehler findet bekommt 10 Euro :)
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • RE: NSArrayController - nicht definierbares Verhalten

    Hmm, kommt mir alles ein wenig von hinten durch die Brust ins Auge vor. Eigentlich sollte sich der ganze Kram auf einen Einzeiler reduzieren lassen:

    Quellcode

    1. [fileCardsController setContent:[[SelectedFileBox selectedFileBox] fileCards]];
    Keine Subklasse von NSTableView oder sonst was nötig. Oder habe ich da irgendwas falsch verstanden?

    Michael
  • Original von Objcler
    Ja tut es und Preserves selection ist aktiviert.

    Dann liegt bei Deinem Code etwas anderes im Argen. Für die beschriebene Aufgabe solltest Du im Model nichts als Accessoren brauchen - den Rest sollte der Controller erledigen. Das Model sollte nicht einmal Kenntnis von der Existenz des Controllers haben müssen. Das ganze Abgreifen von Events usw. ist eigenartig, denn Bindings sind eigentlich dafür da, dass man sowas nicht machen muss.

    Eigentlich kümmert sich der ArrayController auch um ein vernünftiges Selection-Verhalten. Hast Du die selectionIndexes des TableView an die selectionIndexes des ArrayControllers gebunden?
    Multigrad - 360°-Produktfotografie für den Mac
  • "Hast Du die selectionIndexes des TableView an die selectionIndexes des ArrayControllers gebunden?"

    Habe ich nun mal ausprobiert. Folge: Die TableView zeigt keine Daten mehr an, obwohl der ArrayController Daten enthält.

    Nachtrag: [[SelectedFileBox selectedFileBox] fileCards] kontaktiert beim aufruf die Datenbank und sucht die Karteikarten des aktuell selektierten Karteikastens. Es gibt ein NSArray mit FileCard Objekten zurück. Dies wird ja dann zum "Content" des ArrayControllers... eigentlich irgendwie logisch, dass er dann seine "selection" verliert. Oder?
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].