NSOutlineView - Sortierung wiederherstellen nach Änderung

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • NSOutlineView - Sortierung wiederherstellen nach Änderung

    Guten Abend!

    Ich habe gerade ein kleines Problem mit einem Outlineview. Die Daten werden programmatisch via Data-Source Methoden ermittelt, zu Grunde liegt aber letztendlich ein ArrayController der im XIB instantiert wurde und an den managedObjectContext gebunden ist. Der OutlineView darf unterschiedlich sortiert werden, die jeweiligen Sort-Descriptoren werden aus einer Action heraus ebenfalls programmatisch gesetzt.

    Das klappt soweit auch alles wunderbar, bis der Anwender im OutlineView den Inhalt einer Zelle ändert, so dass die Sortierung nicht mehr stimmt (aus "Anton" wird "Zacharias") - der Eintrag bleibt (nachvollziehbarerweise) an der alten Stelle stehen. Im Prinzip würde ein einfaches [listController rearrangeObjects] gefolgt von [outlineView reloadData] ausreichen, und alles wäre wieder richtig sortiert, nur wo?

    Ich habe mal auf NSControlTextDidEndEditingNotification gelauscht, und dort das rearrangeObjects angestoßen, das scheint aber zu früh zu sein, der listController hat zu dem Zeitpunkt die Änderung noch nicht mitbekommen. Wie macht man denn sowas richtig?
  • Vielleicht habe ich mich da missverständlich ausgedrückt. Ich mache schon alles in der Controllerschicht, meine Frage war mehr die Richtung: Wie bekommt die Data-Source mit, dass der Anwender im Outline-View einen Eintrag umbenannt hat? Der OutlineView hat keine entsprechende Delegate Methode, in der Vererbungskette habe ich bei NSControl jedoch was passendes gefunden (controlTextDidChange). Die Methode habe ich in der DataSource überschrieben, nur konnte ich von dort keine Neusortierung machen, weil der ArrayController noch nichts von der Umbennenung mitbekommen hat (irgendwie ist mir Core-Data in der Richtung noch etwas unsympathisch, benimmt sich manchmal etwas träge).

    Wie dem auch sei, ich habe es jetzt gelöst indem ich in controlTextDidChange statt der Neuortierung ein performSelector:withObject:afterDelay:0.0 aufgerufen haben, und erst in der Selektor-Methode umsortiere. Scheint gut zu funktionieren, richtig sauber empfinde ich es subjektiv aber nicht.
  • Auf jeden Fall sendet der Field-Editor eine Notification. Du könntest dir auch einen eigenen bauen.

    Aber die Problematik liegt einfach darin, dass die Änderung des Models $überall her kommen kann und deshalb du das auch so einrichten musst und ein Anhängen an das UI nicht die Lösung sein sollte.

    Du musst das also schon so machen, dass jede Änderung der Modelebene zu einer Neusortierung führt. Dazu ist Observierung gut geeignet. Ein Array-Controller hat dafür IIRC aber auch eine Option!?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Danke für Deine Antwort, das geht wahrscheinlich eher in die richtige Richtung!

    Amin Negm-Awad schrieb:

    Ein Array-Controller hat dafür IIRC aber auch eine Option!?

    Gut zu wissen, da gibt es in der Tat ein Flag AutomaticallyRearrangesObjects. Das scheint auch zu funktionieren, ist aber nur ein Teil der Lösung, da ich auch in dem Fall noch immer der OutlineView angestoßen werden muss (reloadData). D.h. um eine Observierung werde ich nicht herumkommen, muss mir aber das Kapitel erst noch einmal durchlesen. Observieren müsste ich dann eigentlich die arrangedObjects vom ArrayController, oder?
  • Falls die Änderungen ausschließlich im OutlineView erfolgen (Dich darauf zu verlassen ist, wie Amin schon sagte, heikel - Änderungen könnten ja von woanders her kommen), kannst Du den reload auch in

    Quellcode

    1. - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item

    deiner Datasource anstoßen. Die ganz sichere Variante ist, alle Deine Modell-Items auf der entspr. property zu observieren (nur arrangedObjects Deines AC reicht leider nicht, denn dann bekommst Du Änderungen an den properties Deiner items nicht mit, sondern nur, dass sich die Anzahl geändert hat) und dann im Controller den reload anzustossen.
  • Markus Müller schrieb:

    Jep! Wieso verwendest Du denn keinen NSTreeController?
    Hauptsächlich weil ich im NSTreeController kein filterPredicate setzen kann, und ich finde man kann einen OutlineView auch mit dem normalen NSArrayController mit einer Data-Source und ein paar Zeilen Code ganz gut versorgen (gerade wegen dem filterPredicate!). Hätte denn der NSTreeController in Bezug auf mein Problem einen Vorteil?

    Markus Müller schrieb:

    Die ganz sichere Variante ist, alle Deine Modell-Items auf der entspr. property zu observieren (nur arrangedObjects Deines AC reicht leider nicht, denn dann bekommst Du Änderungen an den properties Deiner items nicht mit, sondern nur, dass sich die Anzahl geändert hat) und dann im Controller den reload anzustossen.
    In meinem derzeitigen Mini-Projekt kann ich eigentlich Änderungen von außerhalb des Views auschließen, daher sollte der reload in setObjectValue ausreichend sein (guter Tipp übrigens, Danke! Da schreibe ich selber ja schon den Wert zurück ins Model, das hatte ich ganz verdrängt - Schande über mich).

    Aber ich will das Projekt ja auch zum lernen verwenden - daher möchte ich die Observierung auch noch testen. Im Prinzip muss ich ja das entsprechende Property jeder einzelnen Item-Instanz überwachen, brauche also jedes Item einmal im Zugriff. Wenn der Inhalt bei jedem Programmstart neu aufgebaut werden würde, könnte ich in meiner "addNewEntry"-Routine die Observierung anmelden, und in der "deleteEntry"-Routine abmelden.

    Aber das macht ja keinen Sinn, die Einträge können ja auch aus einer Datei geladen werden. Im Prinzip müsste ich mich dann doch dort wo Core-Data ein Item instantiert reinhängen und sagen ich möchte observieren, und an ebenso geeigneter Stelle mich auch wieder abmelden. Muss ich mich dann irgendwo mein ManagedObjectContext reinhängen, um die Instantierung eines neuen Objekts mitzubekommen? Oder wo macht man das besser?