NSCell draw... bei Spalten-verschieben

  • NSCell draw... bei Spalten-verschieben

    Wenn ich einen NSTableView habe, werden ja die draw...-Methoden der NSCells recht häufig aufgerufen (Ändern der Zellenwerte, Größenänderung des Fensters, Auswählen einer Zelle, Größenänderung der Spalte, etc.) aufgerufen. So weit, so vernünftig.

    Jetzt habe ich in meiner NSCell-Subklasse einen NSProgressIndicator hinzugefügt (als Subview im ControlView). Funktioniert soweit auch alles ganz gut, bis auf einen Sonderfall: das Verschieben der Spalte. Also wenn ich beispielsweise die Spalte links von der Fortschrittsspalte verkleinere oder vergrößere, dann wird die Fortschrittsspalte nicht neu gezeichnet, weil sie ja nur verschoben wird, aber sich nicht ändert. Der restliche Inhalt der Zelle (mit draw hinzugefügt) wird auch korrekt mit nach links oder rechts verschoben. Nur der ProgressIndicator bleibt bis zum nächsten draw an der alten Position kleben (wahrscheinlich weil es ein Subview ist).

    Gibt es eine einfache Möglichkeit das Problem zu beheben? Also zum Beispiel irgendwie mitbekommen, wann sich die Spalte ändert und dann manuell die draw...-Funktion der Zelle anzustoßen?
  • Schau Dir mal das Apple Sample animatedtableview an, da wird das gezeigt.

    developer.apple.com/library/ma…e_ref/doc/uid/DTS40008863

    Die Idee ist, ein Set an sichtbaren subviews vorzuhalten und dieses entsprechend zu aktualisieren. Dafür musst Du NSTableView ableiten und -viewWillDraw entspr anpassen.

    Ich habe genau so etwas anhand des Demos von Apple mal umgesetzt, kann es morgen Abend posten, wenn ich wieder schnelles Internet habe.

    Gruß, Markus

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Markus Müller ()

  • alexlaske schrieb:

    Hmm, Du könntest es mal mit den Delegate-Methoden versuchen. Da gibt es columnDidResize. Da könntest Du nach der Spalte vor Deinem Indicator fragen und dann reloadData aufrufen. Ich weiß gerade nicht aus dem Kopf, ob es auch reloadColumn gibt..
    Das Problem ist, dass es columnDidResize ist und kein columnWillResize ist … die Delegate-Methode also erst aufgerufen wird, nachdem die Größenänderung komplett abgeschlossen ist, also quasi wenn der MouseUp erfolgt. Somit hab ich während des Vorgangs keine Rückmeldung auf die ich reagieren kann.

    Markus Müller schrieb:

    Schau Dir mal das Apple Sample animatedtableview an, da wird das gezeigt.

    developer.apple.com/library/ma…e_ref/doc/uid/DTS40008863

    Die Idee ist, ein Set an sichtbaren subviews vorzuhalten und dieses entsprechend zu aktualisieren. Dafür musst Du NSTableView ableiten und -viewWillDraw entspr anpassen.

    Ich habe genau so etwas anhand des Demos von Apple mal umgesetzt, kann es morgen Abend posten, wenn ich wieder schnelles Internet habe.

    Gruß, Markus
    Danke für das Beispiel. Allerdings hat mir das irgendwie nicht weiter geholfen – weiß nicht, vielleicht stehe ich da auch auf dem Schlauch ;) Also ich habe schon einen TableView in dem auch in den Zellen die Fortschrittsbalken angezeigt werden. Dies erfolgt wie du erwähnst mit ProgressIndicator-Views, die vorgehalten werden und beim Update der Zellen verwendet werden. Also das funktioniert soweit ja auch.


    Mein Problem ist jetzt aber, dass dies Draw-Methode (welche auch immer) nicht aufgerufen wird, wenn sich die Spalte mit dem Subview nur verschiebt, nicht aber in der Größe verändert. Cocoa denkt sich dann einfach "wenn sie nur die Position der Zelle ändert, dann brauch ich die ja nicht neu zeichnen. Dann nehme ich einfach das Gezeichnete und packe es nur an eine andere Position auf dem Bildschirm". Und ich brauche jetzt irgendeine Möglichkeit zu sagen "Egal. Zeichne es trotzdem."
  • Hab jetzt eine Lösung, die für mich zu funktionieren scheint.

    Im IB die Tabellenspalten so ausrichten, dass diese wirklich ganz genau den TableView ausfüllen. Auf den Pixel genau.

    Dann in der mit windowsWillResize des NSWindowDelegate und tableViewColumnDidResize des NSTableViewDelegate anhand der Größe des Views berechnen wie breit die einzelnen Spalten sein dürfen und wo die jeweiligen Maximal- und Minimalwerte für die Breiten liegen und diese in den Spalten neu setzen. In der Summe müssen so sämtliche Spalten immer genau die Breite der Tabelle ergeben.

    Bei zwei Spalten und einer Breite von 400 der Tabelle … wenn die zweite Spalte einen Maximalwert von 300 hat, dann darf die erste Spalte nur einen Minimalwert von 100 haben. Wenn die zweite Spalte einen Minimalwert von 50 hat, darf die erste Spalte nur einen Maximalwert von 350 haben. So hab ich es geschafft, dass Cocoa die Spalten immer in der Größe verändert (links und rechts vom Divider) und nicht auf die Idee kommt nur die linke Spalte in der Größe zu ändern und alles rechts davon einfach nur zu verschieben.

    Also nicht wirklich eine Lösung für das Problem: Draw bei Spalten-verschieben anzustoßen, aber zumindest eine Lösung für mein Darstellungsproblem.
  • Um einer Cell einen ProgressIndicator hinzuzufügen. Das war zumindest das Vorgehen, das mir am sinnvollsten erschien (auch wenn es dem eigentlichen System der Tabellenzellen entgegen steht). Habe auch im Internet keine alternative Möglichkeit gefunden das zu realisieren.
  • MarkusB schrieb:

    Um einer Cell einen ProgressIndicator hinzuzufügen. Das war zumindest das Vorgehen, das mir am sinnvollsten erschien (auch wenn es dem eigentlichen System der Tabellenzellen entgegen steht). Habe auch im Internet keine alternative Möglichkeit gefunden das zu realisieren.

    Eine Cell ist kein View und hat demgemäß auch keine Subviews. Man kann so etwas manuell verwalten, aber das gibt doch nur Ärger.

    Einfacher ist es, Subcells zu nehmen, also eine neue Cell zu definieren, die eine weitere Cell hat. In meinem Buch gibt es dafür Code. Aber unter ImageAndTeextCell dürftest du auch im Netz zahlreiche Lösungsvorschläge finden.
    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"?
  • Amin Negm-Awad schrieb:

    Eine Cell ist kein View und hat demgemäß auch keine Subviews. Man kann so etwas manuell verwalten, aber das gibt doch nur Ärger.

    Einfacher ist es, Subcells zu nehmen, also eine neue Cell zu definieren, die eine weitere Cell hat. In meinem Buch gibt es dafür Code. Aber unter ImageAndTeextCell dürftest du auch im Netz zahlreiche Lösungsvorschläge finden.
    Das cell != view meinte ich ja damit, dass die View-Variante dem System der Tabellenzellen entgegen steht.

    Mit Cell in Cell hab ich jetzt noch nicht herumgespielt, aber ich seh gerade auch nicht, wie mir das weiterhilft, da der NSProgressIndicator ja nunmal ein View ist. Und wenn ich nach ImageAndTextCell suche, dann finde ich vor allem Beispiele, wie man Text und Bilder in der Zellen draw-Methode selber mit Methoden wie drawAt... in den Grafikkontext der Zelle zeichnet. Aber bei einem animierten View eines NSProgressIndicators wird es da schon schwieriger.

    Deshalb: entweder verstehe ich nicht, wie mir dein Vorschlag helfen könnte oder für einen animierten NSProgressIndicator in einer Cell gehts nicht anders als mit manuell verwalteten Subviews.
  • Dreck, Beitrag gelöscht.

    Nur einmal kurz:

    *Boing* Verstehe, es gibt keine PICell. Ja, Problem.

    Aber eine Cell hat ja nun einmal keinen Frame. Man kann den nur "erfahren", wenn man sich den im -draw… merkt. Wenn das der Tableview wegoptimiert, kannst du es in der Beziehung (NSTableView-NSCell) nicht herausbekommen. Wenn du also einen View hereinlegst, der braucht ja einen Frame, stößt sich das konzeptionell. Wirklich gut kann das nicht funktionieren, sondern nur imt Gefrickel und äußerer Hilfe.

    Bevor ich aber mit Subviews herumhantiere, würde ich mir eine PICell selbst programmieren oder im Internet suchen. So ungewöhnlich ist dein Problem ja nicht, dass das nicht schon einmal jemand gemacht haben sollte.

    Die Animation kannst du dir ja über einen Timer besorgen. Das sollte nicht das Problem sein.

    Sag mal, du müsstest dann aber doch so viele Subviews wie Zeilen haben? Richtig? Das ist aber ein richtiges Tableview, nicht nur eine Liste wie etwa das Downloadfenster in Safari? Da würde ich gar nicht erst zu einem Tableview greifen.

    Lade mal einen Screenshot hoch.
    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"?
  • Bequem wie ich bin, hab ich ja im Internet nach NSCell und Progress Indicator gesucht. Die Vorschläge gingen alle eigentlich in die gleiche Richtung. Egal ob Balken oder Spinning Wheel. Wenn Lion doch eine größere Verbreitung hätte … TableCellViews sind soo komfortabel.

    Hier mal ein Screenshot. Es ist schon ein TableView und nicht einfach nur ein Safari-Downloads-ScrollView. Es können auch noch mehr Spalten hinzugefügt werden, etc.
  • Ich habe sofort für eine PICell bei Google gefunden, der mir allerdings vom Code nicht behagte.

    Für das im Screenshot würde ich aber überhaupt kein Tableview nehmen. Brauchst du Sortierung, Filterung? Brauchst du überhaupt veränderliche Spalten?

    (Klassische) Cells sind beschissen, schreibe ich ja auch. Aber sie sind nun einmal da.
    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"?
  • Zuerst habe ich mit einem ScrollView gearbeitet, da ich aber nach und nach immer mehr der Funktionen eines TableViews nachbauen musste (also auch nicht so die Ideallösung), hatte ich mich dann entschieden, doch einen echten TableView zu benutzen.

    Kannst du mal den Link posten, den du meinst … selbst wenn der Code nicht so optimal zu sein scheint, aber vielleicht bekomme ich dann eine bessere Vorstellung davon was du meinst.

    So lange man nicht auf TableCellViews in 10.7 setzt, sondern dieses Verhalten in 10.6 nachbauen muss, denke ich, dass es keine Lösung gibt die wirklich alle zufrieden stellt. Egal welche Lösung man dann präferiert, irgendwo muss man immer frickeln. Entweder bei Views oder bei Sortierung, Filterung, Auswahl, etc. Das einzig wirklich unschöne in meiner Variante ist halt, dass jedes Objekt im ArrayController einen ProgressIndicator behält, sofern es einen Fortschritt anzuzeigen gibt. Und in der jeweiligen Draw-Methode wird dann der PI einfach als Subview auf den controlView der Zelle gesetzt. Sicher nicht die optimalste Lösung, aber eine eine Lösung die funktioniert.
  • MarkusB schrieb:

    Zuerst habe ich mit einem ScrollView gearbeitet, da ich aber nach und nach immer mehr der Funktionen eines TableViews nachbauen musste (also auch nicht so die Ideallösung), hatte ich mich dann entschieden, doch einen echten TableView zu benutzen.

    Kannst du mal den Link posten, den du meinst … selbst wenn der Code nicht so optimal zu sein scheint, aber vielleicht bekomme ich dann eine bessere Vorstellung davon was du meinst.

    So lange man nicht auf TableCellViews in 10.7 setzt, sondern dieses Verhalten in 10.6 nachbauen muss, denke ich, dass es keine Lösung gibt die wirklich alle zufrieden stellt. Egal welche Lösung man dann präferiert, irgendwo muss man immer frickeln. Entweder bei Views oder bei Sortierung, Filterung, Auswahl, etc. Das einzig wirklich unschöne in meiner Variante ist halt, dass jedes Objekt im ArrayController einen ProgressIndicator behält, sofern es einen Fortschritt anzuzeigen gibt. Und in der jeweiligen Draw-Methode wird dann der PI einfach als Subview auf den controlView der Zelle gesetzt. Sicher nicht die optimalste Lösung, aber eine eine Lösung die funktioniert.

    Weiß nicht mehr, was ich gesucht hatte. aber jetzt finde ich zum Beispiel
    code.google.com/p/vidnik/sourc…gressIndicatorCell.m?r=68

    Dürfte aber auch nicht schön aussehen. Apropos aussehen? Wieso nicht einfach den PI in eine Bitmap schreiben.

    Ja, Cells sind schrecklich, aber immerhin verbirgt sich dahinter ein System. Und dagegen zu programmieren, finde ich fürchterlich.

    Inzwischen ist ja vieles von den Features eines Table-Views im Array-Controller gelandet. Ich weiß nicht, wie schnell du dir eine Observierung/Bindings programmierst. Aber Sortierung und Filterung bekommst du damit ja für lau. Auswahl ja eigentlich auch!?

    Eigentlich ist nur die horizontale Anordnung (Spalten), vllt noch Resizing (ist aber ja nun keine Kunst) noch originäres Gebiet eines Table-Views. Sonst sehe ich nichts. Und vort allem: So ein Ding kann man ja immer wieder brauchen.
    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"?
  • Also eine NSLevelIndicatorCell aufgrund der optischen Ähnlichkeit zu verwenden, ist auch ein interessanter Ansatz … auch wenn ich bei mir ein paar Änderungen mehr hätte vornehmen müssen.

    NSCollectionView hatte ich bislang noch gar nicht auf dem Schirm … muss ich mir dann mal in Ruhe anschauen, auch wenn es auf den ersten Blick nicht nach dem aussieht, was ich momentan habe.

    Ich bin halt momenten in einer persönlichen Zwickmühle ;) Auch wenn meine Lösung ein Flickenteppich von diversen Techniken ist um auf einer Cell-Base-Table mit animierten Subviews zu arbeiten. Es hat mich ein bisschen Zeit gekostet, das alles so hinzubekommen. Aber es läuft halt (inzwischen) so wie ich es angedacht hatte (und das war ja meine eigentliche Frage bezüglich eines Problems). Bei anderen Methoden muss ich hier und da was verändern … mein bisheriger Aufwand wäre somit teilweise hinfällig und ob es danach besser, stabiler, komfortabler ist, ist dann ein anderer Punkt. Und nur dass der Code dann etwas besser ist, oder dass der Sinn von Elementen besser beachtet wird als alleiniges Argument für eine Änderung ist mir aktuell ein bisschen zu wenig (wäre ja nicht das erste mal, dass man sich irgendwie anderweitig behelfen muss). Zudem ist die Tabelle ja nur ein Teil in der App … jetzt den Teil umzuändern, würde halt Zeit kosten während die Arbeit in anderen Teilen noch erledigt werden muss. Und ich erwähnte es ja bereits ein paar Mal: meine bisherige Lösung ist unter der Haube sicher nicht optimal, aber sie funktioniert einwandfrei.

    Deshalb belasse ich es jetzt erstmal bei meiner Variante und werde mich gegen Ende der Programmierung eventuell erneut damit beschäftigen :D
  • So, hier wie versprochen ein Beispielprojekt. Es basiert auf dem Apple-Sample AnimatedTableView von Apples TableView-Guru Corbin Dunn (er hat das Lion view-based tableview geschrieben).

    Das Beispiel funktioniert auch unter 10.5, für die Erzeugung ist das Tableview-delegate zuständig. Es werden nur die tatsächlich sichtbaren views vorgehalten, eine CoreData-Anwendung mit tausenden von Entities sollte also damit funktionieren.

    Viel Spaß damit, Markus

    @edit
    Wenn Du der entspr. Spalte einen Idenitifer verpasst und diesen dann berücksichtigst, klappst auch mit dem Nachbarn...:-) Hab das Beispiel aktualisiert, musst Du erneut runter laden.

    Quellcode

    1. - (NSRect)subviewTableView:(MMSubviewTableView *)tableView rectForView:(NSView*)view inRow:(NSInteger)row
    2. {
    3. return [ self progressRectInFrame:[ self.tableView frameOfCellAtColumn:[ self.tableView columnWithIdentifier:@"job" ]
    4. row:row ] ];
    5. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Markus Müller ()

  • Markus Müller schrieb:

    So, hier wie versprochen ein Beispielprojekt. Es basiert auf dem Apple-Sample AnimatedTableView von Apples TableView-Guru Corbin Dunn (er hat das Lion view-based tableview geschrieben).

    Das Beispiel funktioniert auch unter 10.5, für die Erzeugung ist das Tableview-delegate zuständig. Es werden nur die tatsächlich sichtbaren views vorgehalten, eine CoreData-Anwendung mit tausenden von Entities sollte also damit funktionieren.
    Danke … aber das Beispiel hat trotzdem das gleiche Problem, wie das, was ich eingangs beschrieben hatte. Wenn ich nämliche die zweite Spalte verschiebe (und nicht vergrößere) bleibt der ProgressIndicator-Subview erstmal an seiner alten Position hängen.
  • MarkusB schrieb:

    Markus Müller schrieb:

    So, hier wie versprochen ein Beispielprojekt. Es basiert auf dem Apple-Sample AnimatedTableView von Apples TableView-Guru Corbin Dunn (er hat das Lion view-based tableview geschrieben).

    Das Beispiel funktioniert auch unter 10.5, für die Erzeugung ist das Tableview-delegate zuständig. Es werden nur die tatsächlich sichtbaren views vorgehalten, eine CoreData-Anwendung mit tausenden von Entities sollte also damit funktionieren.
    Danke … aber das Beispiel hat trotzdem das gleiche Problem, wie das, was ich eingangs beschrieben hatte. Wenn ich nämliche die zweite Spalte verschiebe (und nicht vergrößere) bleibt der ProgressIndicator-Subview erstmal an seiner alten Position hängen.

    habs angepasst, s.o.