Kapitel 6 - Inspector Toolbar

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

  • Kapitel 6 - Inspector Toolbar

    Ich habe ein Problem mit dem Beispiel in Kapitel 6, dem Umschalten eines TabViews im Inspector-Panel via Toolbar. Das umschalten von Hand funktioniert wunderbar, sobald ich jedoch folgenden Abschnitt hinzufüge stürzt das Programm ab (Seite 508 :(

    Quellcode

    1. -(void)awakeFromNib
    2. {
    3. // Setze Item mit Index 0
    4. NSToolbar* toolbar = [[self window] toolbar];
    5. ...
    6. }


    Setze ich eine Breakpoint auf dieser ersten Zeile und versuche mit "Step Over" weiterzugehen, springt der Debugger immer wieder an den Anfang von awakeFromNib, befindet sich also in einer Endlosschleife. Wenn ich das Programm dann abbreche erscheint in xCode eine Meldung "loading xxx stack frames", wobei xxx eine ziemlich hohe Zahl ist, je nachdem wie lange die Endlosschleife lief bis zum Abbruch.

    Ich stehe gerade auf dem Schlauch, was könnte ich falsch gemacht haben, so dass [[self window] toolbar] immer wieder awakeFromNib neu aufruft?
  • Ah, eben erst die zweite Antwort gesehen :) Ich kann auch das Projekt hochladen, danke für das Angebot. Leider jedoch erst heute Abend (derzeit Hardware mit XCode <> Hardware mit Internetzugang). Ich vermute am ehesten dass ich irgendwo im InterfaceBuilder ein Fehler gemacht habe und irgendwas im Kreis delegiere oder so.
  • So, anbei nun das aktuelle Projekt, ich habe gestern noch etwas weiter gemacht, der Stand ist nun 106-17: osxentwicklerforum.de/index.ph…fbec7100eb47ea232ebbc1da4

    Ich habe noch einige zusätzliche Fehler in meiner Version gefunden:

    1. In InspectorWC führt der Zugriff auf die toolbar in Methode awakeFromNib zu einer Endlosschleife (wie zuvor beschrieben).

    2. Etwas später im Kapitel 6 wird das Binding in MyDocument.xib gegen die Data-Source ausgetauscht, das Textfeld wird dann z.B. an den File's Owner (GroupWC) mit Model Key Path "selectedGroup.name" gebunden. Dies führt zu folgender Fehlermeldung beim Start des Programms: "[<NSOutlineView 0x1001796a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name."

    Ich habe im TextField Binding das Flag bei "Raises For Not Applicable Keys" gelöscht, das Binding funktioniert problemlos und der Fehler ist weg, aber ich denke das Ziel sollte eher sein die Klasse GroupWC key value coding-compliant zu machen. In der Musterlösung ist das Flag auch gesetzt und ich habe beim händischen Vergleich keine Unterschiede zur Musterlösung gefunden :(

    3. Der Inspector erscheint nur noch einmal. Wenn ich das Fenster öffne, wieder schließe und dann verscuhe über das Menü den Inspector erneut zu öffnen passiert nichts. ShowInspector in AppDelegate wird durchlaufen, führt jedoch zu nichts.


    Ich habe mich in diesem Projekt eigentlich ziemlich an die Buchvorgaben gehalten (habe ein zweites Projekt für Spielereien). Die einzige Änderung habe ich in GroupWC addGroup vorgenommen ( Buch Seite 528 ), ich glaube aber nicht dass das was mit den Problemen zu tun hat:

    Quellcode

    1. [sidebarView selectRow:[sidebarView rowForItem:groupMO] byExtendingSelection:NO];


    Der Compiler hat hier gemeldet das selectRow "deprecated" ist (zwar nur als Warnung, aber ich habe im Buch ja gelernt dass ich jede Warnung wie einen Fehler behandeln soll). Laut Doku soll stattdessen selectRowIndexes verwendet werden, also habe ich folgendes gemacht.

    Quellcode

    1. NSIndexSet* rowIndex;
    2. rowIndex = [[[NSIndexSet alloc] initWithIndex:[sidebarView rowForItem:groupMO]] autorelease];
    3. [sidebarView selectRowIndexes:rowIndex byExtendingSelection:NO];
  • Problem 2) hat sich erledigt. Ist imho ein Fehler im Buch. Auf Seite 535 wird in -outlineView:shouldSelectItem die Zeile "self.selectedGroup = item;" hinzugefügt, was in meinem Fall auch passieren konnte wenn item eine Person war - dann gibt es natürlich auch kein Attribut "name". In den Projekten auf der Homepage ist diese Zeile nicht zu finden (und dürfte dort auch nicht notwendig sein, wird ja in -outlineViewSelectionIsChanging bereits gesetzt). Auch wird die Zeile - sofern ich nichts übersehen habe) im Buch später nicht mehr entfernt.

    Die anderen beiden Probleme bleiben bestehen - ich suche natürlich parallel auch weiter ...
  • Hallo!

    das Projekt hatte ich schon hochgeladen (siehe meine vorletze Antwort; erste Zeile; CompanyCD.zip). Es macht aber wahrscheinlich Sinn dass die nochmals eine aktualisierte Version hochlade, da ich einige Fehler schon selber behoben habe und eine neue Version somit das Testen einfacher macht. Kann ich heute Abend wieder machen.

    Der Source-Code der Musterlösung welche man hier runterladen kann ist richtig und funktioniert, es gibt nur immer mal wieder Stellen wo der im Buch abgedrucke Source-Code von dem Source-Code in der fertigen Musterlösung abweicht. So musste ich zum Beispiel auch an den bereits in diesem Thread Stellen Anpassungen vornehmen, damit es überhaupt funktioniert.

    "Release When Closed" ist übrigens gesetzt.
  • Tut mir leid, habe ich wohl übersehen. Ich habe mir deinen Source-Code einmal kurz angeschaut. In der Nibname Inspector hast du vergessen, deinen Window mit der Controller Instanz zu verbinden. Also einfach von File's Owner und Panel eine Verbindung ziehen und das entsprechende IBOutlet window auswählen.
    Dann musst du noch folgende Methode anpassen in deinem AppDelegate:

    Quellcode

    1. - (IBAction)showInspector:(id)sender {
    2. ...
    3. [self.inspectorWC showWindow:nil];
    4. }

    Jetzt sollte es eigentlich soweit funktionieren. Hast du noch weitere Probleme?
  • Pascal Giessler schrieb:

    Ich habe mir deinen Source-Code einmal kurz angeschaut. In der Nibname Inspector hast du vergessen, deinen Window mit der Controller Instanz zu verbinden. Also einfach von File's Owner und Panel eine Verbindung ziehen und das entsprechende IBOutlet window auswählen.

    Aaargh, diese eine kleine Verbindung hat sowohl Problem 1 (Toolbar ist nicht ansprechbar) und Problem 3 (Fenster lässt sich nach dem schließen nicht wieder öffnen) gelöst. Ich Danke Dir ganz herzlich, und den Fehler mache ich bestimmt kein zweites Mal mehr ;(

    showWindow:self musste ich gar nicht ändern, funktioniert so schon, hat es irgendwelche Vorteile stattdessen nil zu nehmen? In der Dokumentation steht nur "sender - The Control sending the message; can be nil.".

    Ansonsten teste ich jetzt wirklich erstmal durch, finde bestimmt noch das eine oder andere Problemchen ;)
  • LbbnPx schrieb:

    Pascal Giessler schrieb:

    Ich habe mir deinen Source-Code einmal kurz angeschaut. In der Nibname Inspector hast du vergessen, deinen Window mit der Controller Instanz zu verbinden. Also einfach von File's Owner und Panel eine Verbindung ziehen und das entsprechende IBOutlet window auswählen.

    Aaargh, diese eine kleine Verbindung hat sowohl Problem 1 (Toolbar ist nicht ansprechbar) und Problem 3 (Fenster lässt sich nach dem schließen nicht wieder öffnen) gelöst. Ich Danke Dir ganz herzlich, und den Fehler mache ich bestimmt kein zweites Mal mehr ;(

    showWindow:self musste ich gar nicht ändern, funktioniert so schon, hat es irgendwelche Vorteile stattdessen nil zu nehmen? In der Dokumentation steht nur "sender - The Control sending the message; can be nil.".

    Ansonsten teste ich jetzt wirklich erstmal durch, finde bestimmt noch das eine oder andere Problemchen ;)

    Das nil ist vergleichsweise gleichgültig, da der Parameter in diesem Falle nicht ausgewertet wird.
    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"?
  • Vielen Dank nochmals für die Hilfe, habe das Kapitel 6 durch und alles klappt soweit. Ich habe zum verdauen noch etwas experimentiert, hier haben sich noch zwei Fragen ergeben die zwar nicht ganz zum Threadthema passen, aber ich wollte dafür jetzt auch nicht gleich einen neuen aufmachen.

    1) Am Ende von Kapitel 6 steht "Sie können hier übrigens noch experimentieren: Wie wäre es, wenn Sie auch das Dragging auf die Mitgliederliste einer Gruppe zulassen?". Gesagt, getan! War nicht ganz so trivial wie ich zunächst gedacht hatte, da ja die Mitgliederliste in einem ganz anderen View (und in einer anderen xib) liegt als der outlineView. Daher muss hierfür wohl ein ViewController her. Ich habe diesen angelegt, im Windows Controller (GroupsWC) diesen instanziert (anstelle des Default-NSViewController), ihn in meiner XIB als File's Owner eingetragen, für die Data-Source verantwortlich gemacht und die tableView:validateDrop und tableView:acceptDrop Methoden überschrieben. An der Stelle musste ich mir dann die Frage stellen ob den im outlineView (der ja im Window Controller definiert ist) überhaupt eine Gruppe ausgewählt ist, und falls ja welche. Wie komme ich also aus dem ViewController an den WindowsController.

    Etwas Recherche hat ergeben dass ich diesen über "GroupsWC* wc = self.representedObject;". Meine eigentliche Frage ist: "Warum?". Also ich meine, warum ist representedObject mein Window Controller, ich habe doch nirgends eine Verbindung gesetzt, bzw. die einzige Verbindung ist dass ich den ViewController aus dem WindowController heraus instanziiert habe. Ich versuche das nur zu verstehen, was ich verstanden habe kann ich mir besser merken.

    BTW: Funktionieren tut es einwandfrei.

    2) Der OutlineView sah noch etwas langweilig aus, also wollte ich noch ein paar hübsche Icons vor die Einträge hängen, so wie man es aus anderen Programmen kennt. Das nachfolgende ist etwas wild zusammenkopiert, funktioniert aber:

    Quellcode

    1. GroupWC.h
    2. @interface GroupsWC : NSWindowController {
    3. ...
    4. // Icons
    5. NSImage* personImage;
    6. NSImage* groupImage;
    7. }


    Quellcode

    1. GroupWC.m- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
    2. {
    3. NSBrowserCell* aCell = [[[NSBrowserCell alloc] init] autorelease];
    4. [aCell setLeaf:YES]; // Vermeiden des Pfeils auf der rechten Seite der Cell
    5. return aCell;
    6. }
    7. - (void) outlineView:(NSOutlineView*)olv
    8. willDisplayCell:(NSCell*)cell
    9. forTableColumn:(NSTableColumn*)tableColumn
    10. item:(id)item
    11. {
    12. if (item != personsPlaceholderItem ) {
    13. if ( [[[item entity] name] isEqualToString:@"Person"] ) {
    14. [(NSBrowserCell*)cell setImage:personImage];
    15. } else if ( [[[item entity] name] isEqualToString:@"Group"] ) {
    16. [(NSBrowserCell*)cell setImage:groupImage];
    17. }
    18. }
    19. }
    20. - (void)awakeFromNib
    21. {
    22. ...
    23. groupImage = [NSImage imageNamed:@"folderImage.tiff"];
    24. personImage = [NSImage imageNamed:@"leafImage.tiff"];
    25. }
    26. - (void)dealloc
    27. {
    28. groupImage = nil;
    29. personImage = nil;
    30. [super dealloc];
    31. }
    Alles anzeigen


    Es werden also zwei Tiffs geladen und vor jeden Eintrag gestellt, sieht das soweit vernünftig aus, oder löst etwas dabei Kopfschütteln aus? Funktionieren tut es ja, habe nur etwas Bedenken ob ich da Speichertechnisch etwas übersehe, insbesondere in dataCellForTableColumn wo ich ständig neue Zellen herausgebe.
  • LbbnPx schrieb:

    Es werden also zwei Tiffs geladen und vor jeden Eintrag gestellt, sieht das soweit vernünftig aus, oder löst etwas dabei Kopfschütteln aus?

    Für diese beiden Bilder solltest Du richtige Properties anlegen und diese dann per Setter setzen. Dein Glück ist im Moment, dass die beiden Bilder, weil Du sie mit der Methode imageNamed: lädst, von Cocoa gecached werden. Wenn du sie stattdessen mit alloc-init-autorelease laden würdest, würden sie vom Autoreleaspool weggeräumt werden.

    LbbnPx schrieb:

    Funktionieren tut es ja, habe nur etwas Bedenken ob ich da Speichertechnisch etwas übersehe, insbesondere in dataCellForTableColumn wo ich ständig neue Zellen herausgebe.

    Wenn die Cell für jede Zeile die gleiche ist, ist es besser sie einmal der NSTableColumn mit der Methode setDataCell: zu übergeben. Die Methode outlineView:dataCellForTableColumn:item: ist dafür gedacht, verschiedene Zellen für einzelne Zeilen/Items zu realisieren.

    Michael
  • Michael schrieb:

    Für diese beiden Bilder solltest Du richtige Properties anlegen und diese dann per Setter setzen. Dein Glück ist im Moment, dass die beiden Bilder, weil Du sie mit der Methode imageNamed: lädst, von Cocoa gecached werden. Wenn du sie stattdessen mit alloc-init-autorelease laden würdest, würden sie vom Autoreleaspool weggeräumt werden.

    Danke für Deine Antwort! Habe auch schon gemerkt das da was nicht stimmt, wenn ich versuche ein Systemicon zu laden (z.B. groupImage = [NSImage imageNamed:@"NSEveryone"]; ) überlebt es nur die Methode, danach bekomme ich ein BAD ACCESS. Werde mal auf Properties umstellen und testen ...

    Michael schrieb:

    Wenn die Cell für jede Zeile die gleiche ist, ist es besser sie einmal der NSTableColumn mit der Methode setDataCell: zu übergeben. Die Methode outlineView:dataCellForTableColumn:item: ist dafür gedacht, verschiedene Zellen für einzelne Zeilen/Items zu realisieren.

    Ich habe gestern schon versucht nur eine Zelle herauszugeben, dann kann ich aber nicht unterschiedliche Icons haben, immer das letzte Icon wird für alle Einträge gesetzt (es gibt im Beispielprogramm Personen und Gruppen für die ich unterschiedliche Icons haben möchte). Im Prinzip könnte ich dann ja einfach zwei Cells als Property vorbereiten und die Icons einmalig zuordnen, danach gebe ich in outlineView:dataCellForTableColumn:item: je nachdem ob das item gerade eine Person oder eine Gruppe ist die passende Cell heraus, so müsste es dann besser sein.

    Naja, ich werde jetzt Kapitel 7 in Angriff nehmen, das Kapitel 6 wird mich aber sicher noch eine Weile beschäftigen, habe das Gefühl dass es fast das wichtigste Kapitel im Buch ist (zusammen mit dem Speichermanagement in Kapitel 3).