Nochmal TableView füllen

  • Original von Tom9811
    Original von gritsch
    Original von HansGerber
    Ja, ich habe nur das String C-Array und den Struct aus dem Objekt entfernt um zu verifizieren ob das die Fehlerursache ist, wie vermutet wurde.
    Am (anscheinend immer noch fehlerhaften) Rest wurde nichts verändert.

    Hans


    ich hab ja auch gesagt dass da rein garnichts stimmt!

    in etwa sollte das so aussehen:


    Quellcode

    1. - (id)init
    2. {
    3. ...
    4. _tableViewWerte = [[NSMutableArray alloc] init];
    5. }
    6. - (void)dealloc
    7. {
    8. ....
    9. }
    10. - (int)numberOfRowsInTableView:(id)tv // nur aus dem kopf - die heist aber so in der art
    11. {
    12. return [_tableViewWerte count];
    13. }
    14. -(id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)row
    15. {
    16. Data *tmpData = [_tableViewWerte objectAtIndex:row];
    17. if ([[aTableColumn identifier] isEqualToString:@"vorname")
    18. {
    19. return [tmpData vorname]; // oder wie auch immer du da drankomsmt
    20. }
    21. return [tmpData nachname]; // oder wie auch immer du da drankomsmt
    22. }
    Alles anzeigen

    *lach* Ich dachte zuerst, dies sei ein Beitrag von Hans und wollte schon "Das sieht dochh gut aus!" schreiben.


    danke - bis auf die fehlenden tabs siehts ja auch gut aus aber das kommt halt davon wenn man das im browser reintippen muss...
  • Ich geb' mal meinen recht schlichten Senf dazu und fang mal gaaaanz oben an.
    Das Prinzip heisst MVC - Model-View-Controller.
    Was Du da machst in "...objectValueForTableColumn..." ist, diese drei Dinge, die separat gehören, zu mischen.

    Mach Dir Dein Model: das Array mit den DatenObjekten
    ---> Das Model verwendest Du ja ggf. anderswo im Programm auch
    Mach Dir den View: das NSTableView, im Interface-Builder
    ---> Wie Du anderswo auch fertige NSTableViews verwendet
    Mach' den Controller: der ist das Delegate des TableViews
    ---> Hier wird's für das, was Du machen willst, individuell, indem Du dem TableView sagst, was in seinen Zellen angezeigt werden soll, (!) wenn er sein Delegate danach fragt. Der Controller verbindet das Model mit dem View.

    Über "Bindings" im IB könntest Du Dir die TableView-Delegate-Methoden übrigens sparen. Nur so nebenbei.

    Schau Dir das mal an. Dieses Beispiel geht mit Core-Data und Bindings zwar über das Hinaus, was Du machst, aber Du bekommst ein Gefühl dafür, wie WENIG Code Du eigentlich bräuchtest. In dem Beispiel bildet CoreData das Model, die Views sind die Views (...), und die Bindings verbinden das Ganze, sind sozusagen der Controller, wenn man so will.
    Benötigter Code: keiner
    Schau' Dir's mal an:
    developer.apple.com/cocoa/coredatatutorial/index.html

    No.
  • Mein lieber Schwan, ihr seid aber streng heute. So funktioniert es jetzt :
    Das Splitten nach MVC (Danke norbi !) ist noch nicht drin, ich wollte es erstmal zum Laufen bringen.

    Quellcode

    1. -(id)tableView:(NSTableView *)liste
    2. objectValueForTableColumn:(NSTableColumn *)aTableColumn
    3. row:(int)row {
    4. Daten *DatenInstanz = [Daten new]
    5. ..... //Daten zuweisen mit Set-Methoden
    6. [DatenInstanz setName:@"Karl-Otto"]
    7. NSMutableArray *entries = [NSMutableArray new] //->kommt dann ins -(id) init
    8. [entries addObject:DatenInstanz]
    9. //[DatenInstanz release]; -> ist fehl am Platz (s.u.)
    10. // return [entries objectAtIndex:row]; //hier ist der eigentliche Fehler
    11. return [DatenInstanz name];
    Alles anzeigen


    D.h. der Fehler lag am falschen "return"-Argument.

    Jetzt läuft es immerhin,von daher Euch allen vielen Dank für Eure Mühe.

    Ich habe danach aus dem NSString *name wieder ein NSString *name[2] gemacht, das funktioniert OHNE JEGLICHES PROBLEM !!! Soviel zu dem Thema.
    Nur weil es ein C-Array und kein NSArray ist, muss es doch per se nicht schlecht sein.
    Mag sein, dass ein NSArray viel besser und effektiver ist. Ich glaub's Euch, ihr habt da viel mehr Erfahrung als ich. ABER ES IST NICHT FALSCH.
    In ein paar Monaten (hoffentlich nicht Jahren) hab ich's dann aber vielleicht auch kapiert, was effektives OO-Programmieren beinhaltet.
    Bis dahin bitte ich um Nachsicht für meine (prozeduralen) Anfängerfehler.

    Hans
  • Original von HansGerber
    Mein lieber Schwan, ihr seid aber streng heute. So funktioniert es jetzt :
    Das Splitten nach MVC (Danke norbi !) ist noch nicht drin, ich wollte es erstmal zum Laufen bringen.

    Quellcode

    1. -(id)tableView:(NSTableView *)liste
    2. objectValueForTableColumn:(NSTableColumn *)aTableColumn
    3. row:(int)row {
    4. Daten *DatenInstanz = [Daten new]
    5. ..... //Daten zuweisen mit Set-Methoden
    6. [DatenInstanz setName:@"Karl-Otto"]
    7. NSMutableArray *entries = [NSMutableArray new] //->kommt dann ins -(id) init
    8. [entries addObject:DatenInstanz]
    9. //[DatenInstanz release]; -> ist fehl am Platz (s.u.)
    10. // return [entries objectAtIndex:row]; //hier ist der eigentliche Fehler
    11. return [DatenInstanz name];
    Alles anzeigen


    D.h. der Fehler lag am falschen "return"-Argument.

    Jetzt läuft es immerhin,von daher Euch allen vielen Dank für Eure Mühe.

    Ich habe danach aus dem NSString *name wieder ein NSString *name[2] gemacht, das funktioniert OHNE JEGLICHES PROBLEM !!! Soviel zu dem Thema.
    Nur weil es ein C-Array und kein NSArray ist, muss es doch per se nicht schlecht sein.
    Mag sein, dass ein NSArray viel besser und effektiver ist. Ich glaub's Euch, ihr habt da viel mehr Erfahrung als ich. ABER ES IST NICHT FALSCH.
    In ein paar Monaten (hoffentlich nicht Jahren) hab ich's dann aber vielleicht auch kapiert, was effektives OO-Programmieren beinhaltet.
    Bis dahin bitte ich um Nachsicht für meine (prozeduralen) Anfängerfehler.

    Hans


    man würde da ja auch kein NSArray verwenden sondern einfach 2 strings. Beides ist also fehl am platz. Und das C-array kann man auch als teilwaeise falsch bezeichnen weil zum beispiel KVC (nicht verwechseln mit MVC) so nicht funktionieren kann (Key-Value-Coding).
  • Ja,

    aber da kann der Hillegass nix für. Solange sich das TableView nicht füllen lies - nein, besser gesagt, solange ich es nicht auf die Reihe bringe, das TableView befüllen zu lassen, habe ich keinen Focus auf retain / release / autorelease etc. gelegt.
    ich könnte mich ja auch herausreden und sagen, ich verwende GarbageCollection.:sick:h

    Die vorige von mir präsentierte Lösung bringt mir aber auch nur einen Eintrag ins TableView (zu früh gefreut), ein zweiter erscheint mir gar nicht, wie auch, wenn ich [DatenInstanz name] als return zurückgebe. Irgendwie muss ja doch ein objectAtIndex: row zurückgegeben werden...

    Nenene, ich lösche jetzt den ganzen Krampf, und probiers nochmal in aller Ruhe ganz von vorne. Wie gritsch schon sagte, da stimmt rein gar nichts, das hängt nicht nur an einer Stelle. Recht hat er (leider).

    Hans
  • Original von HansGerber
    Das Splitten nach MVC (Danke norbi !) ist noch nicht drin, ich wollte es erstmal zum Laufen bringen.

    Weißt du, wie du dir ganz viel Frust ersparen kannst?
    Du schaust dir zuerst die Dinge wie MVC und Speicherverwaltung an und probierst dann, das zum Laufen zu bringen.

    Auf deine jetzige Vorgehensweise kommst du vielleicht zum Ziel, aber der Aufwand steht in keinem Verhältnis zum Ergebnis.

    Wie Norbi schon schrieb geht eine einfache Auflistung von Vor- und Nachnamen komplett ohne eine einzige Zeile Code.

    Versuche bitte zu verstehen, dass du es (fast) nur mit Objekten zu tun hast.
    Objekte, nicht dieses Chaos in C++ ;)
    (Mit nem Struct würde ich zum Beispiel gar nicht mehr arbeiten sondern mir gleich ein Objekt basteln. Auch nähme ich lieber ne NSNumber als nen Int...)
    So kannst du mit ganz wenig Aufwand z.B. dein Datenmodell um das Struct erweitern.
    Eigentlich alles ganz einfach.

    Zum 'new' gibts übrigens viele Diskussionen, wie diese.

    Was genau hast du denn vor?
    Wofür brauchst du z.B. den Index?

    Versuch mal das CoreData Tutorial mit Vor- und Nachname, dann passe es um 'istVorhanden' an.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Weißt du, wie du dir ganz viel Frust ersparen kannst?
    Du schaust dir zuerst die Dinge wie MVC und Speicherverwaltung an und probierst dann, das zum Laufen zu bringen.

    Auf deine jetzige Vorgehensweise kommst du vielleicht zum Ziel, aber der Aufwand steht in keinem Verhältnis zum Ergebnis.


    Bin schon dabei, habe den ganzen Unsinn gelöscht und schaue mir jetzt in aller Ruhe diverse Beispiele für TableView an. Hoffe, beim nächsten Anlauf klappt es dann besser.

    Muss den mentalen Umstieg von "Objective-C" auf "Objective-C" noch schaffen.


    Danke
    Hans
  • Sooo, habe mich jetzt mal genauer mit dem TableView-Beispiel von Hillegass beschäftigt.
    Da ich für mein Projekt natürlich das Beispiel natürlich nicht 1:1 übernehmen kann, würde ich mich gerne rückversichern, ob ich alles richtig verstanden habe :

    1) Ich kreiere eine Objective-C Klasse, in der ich meine Datenstruktur festlege. Anstelle des C-String Arrays name[2] definiere ich 2 NSStrings name_german und name_english, je nach Spracheinstellung soll das TableView dann mit den deutschen bzw. englischen Begriffen geüllt werden.

    2) Anstelle des C-structs erzeuge ich ein separates Objekt, das ich als Instanz in die erstgenannte Datenstruktur integriere.

    3) Ich integriere einen NSArrayController, der mir das Einfügen der Elemente für das TableView steuert.

    4) In der (bei mir) einzigen Kolumne im TableView stelle unter "Bindings" das "Bind to : Array Controller" und bei "Model Key Path" den gewünschten Eintrag "name_german" bzw "name_english" her.

    5) Im Hillegass-Beispiel werden einzelne Einträge durch Drücken eines Buttons zugefügt. Dieser Button ist mittels "Control drag" mit der Action "insert:" mit dem NSArrayController verbunden. Soweit dürfte ich das kapiert haben. Wenn ich in meinem Projekt aber meine Datendatei einlese und die "Datenobjekte" mit den nötigen Werten gefüllt habe, liegen ja alle Objekte auf einmal vor. Mir ist noch nicht klar, auf welchem Weg ich diese jetzt quasi in einem Rutsch ins TableView bringe. Das insert: fügt doch jeweils nur einen Eintrag ein, oder ?

    6) Wenn es mir dank Eurer Hilfe dann gelungen ist, den TableView komplett zu füllen und ich im Programm von Deutsch auf English umstelle, dann sollte der Inhalt komplett entfernt und neu eingelesen werden, diesmal aber mit dem neuen "Model Key Path name_english". Wie lösche ich den View komplett auf einmal (im Prinzip wohl das gleich Problem wie das Füllen) und wie stelle ich den Model Key Path im Quellcode auf den neuen Wert um ?

    Ich hoffe, ich bin ein bisschen weiter als vorgestern und die Fragen sind nicht mehr allzu unsinnig.

    Hans
  • Ich nennen Deine Klasse mal "Personen".
    1) OK
    2) OK bzw. wieso nicht in der selben Klasse? Ist aber im Prinzip egal.
    3) der ArrayController erstellt die Bindings. Das Einfügen ist nur eine weitere Funktion.
    4) Der ArrayController benötigt auch sein "ContentArray"-Binding mit dem Du ihm dem PFAD mitteilst, wo er seine Daten findet, z. B. "File's Owner.personenArray".
    Das "Value"-Binding verwendest Du, um die Interface-Objekte "zu füllen", mit "name_german" oder "name_englisch". Du könntest auch eine Methode -(NSString*)nameLocal in Deine Klasse einbauen, die dann abhängig von der Lokalisierung den englischen oder deutschen Namen zurückliefert.
    5) Du kannst entweder direkt den ArrayController mit insert: oder add: ansprechen, um das NSMutableArray (!) mit Deinen Personen-Objekten zu füllen, oder Du bearbeitest direkt das NSMutableArray. Zweites mußt Du "KVC-Compliant" machen, damit der ArrayController die Änderungen mitbekommt, z. B.
    a) du füllst ein lokales "tempArray" mit den Personen-Objekten
    b) Du setzt das NSMutableArray, das der ArrayController überwacht KVC-Compliant, z. B. in Deinem Controller [self setValue:tempArray forKey:@"myPersonenArray"]. Wenn Du das PersonenArray nicht KVC-konform befüllst, siehst Du im Interface erst etwas, wenn Du dem ArrayController ein add: oder insert: schickst, weil der ArrayController erst mit diesen Aktionen eine Änderung mitbekommt. Im Zweifel ist das eine gute Binding-Testmethode.
    6) Du mußt den View nicht löschen. Dank der Bindings kümmert sich der ArrayController darum, alle Änderngen im Model mit allen Views zu synchronisieren.
    Ein Extrembeispiel wäre:
    Du hast Dein Datenmodell (1x) und sagen wir 5 Views in 5 Fenstern, die alle die selben Daten anzeigen, gerne mit fünf verschiedenen ArrayControllern, die das selbe Datenarray überwachen. Änderst Du in einem View durch Eingabe (oder durch Deinen Code) diese Daten, erscheint die Änderung nach Verlassen des Feldes oder direkt beim Tippen je nach Einstellung "Continuously updates Value", in allen Views OHNE dass Du auch nur eine Zeile Code schreiben musst. "Magie :-)"
    Kurzum:
    I) Du hast Dein Datenmodell
    II) Du hast Deine Bindings-Controller, die Daten und Interface verbinden
    III) Du bearbeitest die Daten ausschließlich KVC-konform
    dann brauchst Du Dich um die Synchronisation zwischen Daten und Views nicht mehr zu kümmern.

    No.
  • Original von Lucas de Vil
    Mit nem Struct würde ich zum Beispiel gar nicht mehr arbeiten sondern mir gleich ein Objekt basteln

    Kommt immer drauf an, wofür. Nicht daß ich mit denen um mich schmeißen würde, aber wenn ich in meinem Projekte-Ordner mit Spotlight nach "struct" suche bleibt die Ergebnisliste nicht leer.
    if (!exit(-1)) fprintf(stderr, "exit call failed. Program will continue\n");
  • Besten Dank für Deine Antwort. Leider habe ich etwas Mühe, Deinen Ausführungen zu folgen.

    Daher mal konkret :

    - Beim Programmstart (awakeFromNib ??) lese ich meine Datendatei ein.
    - Meine Datenstruktur sei mal beispielhaft :

    @interface Daten NSObject {
    NSString * name_german;
    NSString * name_english;
    UInt16 preis;
    }

    Ich definiere die set und get-Methoden, um die Daten zuweisen und abfragen zu können.
    Angenommen die Datendatei hat 3 Einträge :
    Tisch, table, 100
    Tür, door, 75
    Stuhl, stool, 75

    Ich erzeuge :
    NSMutableArray *array;
    array = [[NSMutableArray alloc]init];

    Innerhalb einer Schleife erzeuge ich eine Instanz von Daten :
    Daten artikel = [[Daten alloc]init];
    [daten setName_german:xxx]; ///xxx wäre der String vom Auslesen der Datei
    [daten setName_english......

    Dieses Object füge ich dem NSMutableArray hinzu :
    [array addObject:daten];

    Frage 1 : Ist das bis jetzt richtig ?

    Ich nehme an, dass Du das unter Punkt 5) gemeint hast mit "direktem Bearbeiten" des NSMutableArray. Den folgenden Part habe ich nicht kapiert, sorry.

    b) Du setzt das NSMutableArray, das der ArrayController überwacht KVC-Compliant, z. B. in Deinem Controller [self setValue:tempArray forKey:@"myPersonenArray"]. Wenn Du das PersonenArray nicht KVC-konform befüllst, siehst Du im Interface erst etwas, wenn Du dem ArrayController ein add: oder insert: schickst, weil der ArrayController erst mit diesen Aktionen eine Änderung mitbekommt. Im Zweifel ist das eine gute Binding-Testmethode.


    Frage 2 :
    Ich nehme an, dass mit dem NSMutableArray, das der ArrayController überwacht in meinem Falle o.g. "array" gemeint ist. Was muss ich im IB wo einstellen, damit der Link klar ist und an welche Stelle kommt die Zeile Quellcode.

    Besten Dank
    Hans
  • Datenstruktur, also die Klasse "Daten":
    OK. Setter und Getter am besten mit @property und @synthesize, dann brauchst sie nicht zu schreiben.

    In Deinem ProgrammController (!, eine Subclass von NSObject) (den instanzierst Du der Einfachheit halber in MainMenu.nib):
    legst Du das MutableArray an:
    Im Header:
    #import "Daten.h"
    NSMutableArray *myDatenMutableArray;
    sowie @property NSMutableArray *myDatenMutableArray;
    in "Daten.m"
    @snythesize myDatenMutableArray;

    In -(void)awakeFromNib{} Deines ProgramControllers erzeugst Du myDatenMutableArray:
    myDatenMutableArray = [[NSMutableArray array] retain];
    Daten *artikel = [[[Daten alloc] init] autorelease]; // das Array "besitzt" gleich den artikel
    //Werte zuweisen: ...
    [myDatenMutableArray addObject:artikel];
    (oben sandtest Du an Daten ein Autorelease, durch dass addObject "besitzt" das Array jetzt den artikel)
    // nächster Artikel:
    artikel = [[[Daten alloc] init] autorelease];
    //Werte zuweisen: ...
    ....

    Kurzum: fast richtig ;)

    Unter Punkt 5 meinte ich das direkte Bearbeiten des NSMutableArray.

    ad "nicht kapiert".
    Obiges Füllen des NSMutableArrays ist nicht KVC-konform. Wenn Du es so machst, wirst Du im Interface ein leeres TableView sehen. Füllst Du ein lokales temporäres Array und setzt danach myDatenMutableArray KVC-konform mit "setValue:forKey", ist das KVC-konform und der ArrayController bekommt es mit. Bau Dir am besten Testweise einen Button mit einer add:-Action zum ArrayController in Dein Interface ein. Ein Klick auf den Button würde ein "Nachschauen" des ArrayControllers bei seinem ContentArray erzwingen.

    Frage 2:
    Ja, ich meine o.g. array, wobei "array" ein sauschlechter Name ist ;)
    Quellcode brauchst Du gar keinen.
    Instanzier einen ArrayController in MainMenu.Nib im IB. In dessen Binding-Optionen stellst Du das Content Array auf "DeinProgramController.myDatenMutableArray", also im PopUp-Menü Deinen ProgramController und in ModelKeyPath "myDatenMutableArray". Stell' es dir vor wie einen Pfad.
    Dann bindest Du die Spalten (!) des NSTableView:
    Spalte 1: Binding "value", BindTo: ArrayController, ControllerKey: arrangedObjects, ModelKeyPath: name_german
    Zur Erklärung: Beide Bindings ergeben jetzt zusammen den ganzen Pfad: "DeinProgramController.myDatenMutableArray.name_german".

    No.
  • Sorry, aber ich komm bei Punkt 5 immer noch nicht durch.

    Mal ganz abgesehen von den Warnungen, dass kein "assign", "retain" oder "copy"attribut spezifiziert ist, kann ich keine Option finden im ObjectController, wo ich den Content Array direkt so wie von Dir beschrieben einstellen können.

    Ich wiil nicht vermessen sein, aber denkst Du,e s wäre möglich, mir ein komplettes Mini-Projekt per PM zu schicken, dass eine Textdatei - wie zuvor beschrieben - beim Start einliest und name_german in einem TableView auflistet.
    Ein Button sollte noch drin sein, der auf Englisch umstellt, also die Liste neu mit nam_english füllt. Das wäre schon alles.

    Ich glaube, ich kapier das viel eher, wenn ich ein funktionierendes Beispiel sehe.

    Ich hoffe, das ist nicht zu unverschämt.

    Vielen Dank auf jeden Fall für deine bisherige Hilfe
  • Du nimmst auch keinen ObjectController sondern dessen Subclass ArrayController, da Du ja ein ARRAY von Objekten verwalten willst. Dort findest Du auch das content Array-Binding.

    Du bist vermessen ;)
    Wie wär's, wenn DU Dein Projekt hier zum Download reinstellst (den "build"-Ordner vor dem Zippen bitte löschen). Ich schau's mir dann gern an.

    No.
  • Ich lasse mir auch gerne Projekte schicken – und finde dann nicht die Zeit, nachzuschauen. So ist das halt. Wir sind hier alle nicht für die Forentätigkeit bezahlt.

    Lade es hier hoch. Das erhöht die Wahrscheinlichkeit, dass sich jemand drum kümmert, ganz enorm.
    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"?