2009-09-18 4 views
22

Meine App muss Tabellenzellen mit variabler Höhe haben (da sich in jeder Tabellenzelle die Höhe unterscheidet, muss nicht jede Zelle in der Lage sein, ihre Größe zu ändern).Wie kann ich Tabellenzellen mit variabler Höhe auf dem iPhone richtig machen?

Ich habe eine Lösung, die derzeit funktioniert, aber es ist kludgy und langsam.

Meine aktuelle Lösung:

Bevor die Tabellenzellen gemacht werden, ich berechnen, wie hoch jede Zelle durch den Aufruf Sizing Methoden wie -sizeWithFont:constrainedToSize: auf seine Daten sein muss. Ich addiere dann die Höhen, erlaube eine Auffüllung und speichere das Ergebnis mit den Daten.

Dann, wenn mein UITableViewDelegate die -tableview:heightForRowAtIndexPath: empfängt, arbeite ich aus, welches Element für diese Zelle gerendert wird, und gebe die Höhe zurück, die ich vorher berechnet habe.

Wie gesagt, das funktioniert, aber -sizeWithFont:constrainedToSize: ist sehr langsam, wenn Sie es für Hunderte von Elementen nacheinander tun, und ich fühle, dass es besser gemacht werden kann.

Damit dies funktioniert, musste ich zwei Codeabschnitte verwalten - einen, der die Zellenhöhen berechnen würde, und einen, der die Zellen tatsächlich zeichnen würde, wenn die Zeit gekommen ist.

Wenn etwas über das Modell geändert wurde, musste ich beide Code-Abschnitte aktualisieren, und hin und wieder passen sie immer noch nicht perfekt zusammen, was manchmal zu Tabellenzellen führt, die für einen gegebenen Wert etwas zu klein sind Artikel oder zu groß.

Mein Lösungsvorschlag:

Deshalb möchte ich mit der Vorberechnung die Zellenhöhe zu tun, weg. A) weil es das MVC-Paradigma bricht und B) weil es langsam ist.

Also zieht sich meine Zelle selbst und endet mit der richtigen Zellenhöhe. Mein Problem ist, dass ich keine Möglichkeit habe, der Tabelle die Höhe der Zelle vor dem Zeichnen zu zeigen - zu welcher Zeit es zu spät ist.

Ich versuchte -cellForRowAtIndexPath: von innerhalb -tableView:heightForRowAtIndexPath: zu rufen, aber das bleibt in einer Endlosschleife stecken, da der erste den zweiten irgendwann ruft, und umgekehrt (zumindest habe ich das gesehen, als ich es probierte).

So ist diese Option nicht in Frage.

Wenn ich keine Höhe in der Höhe für Zeilen Delegate Methode angeben, dann geht die Tabellenansicht screwwy. Die Zellen sind die perfekte Höhe, aber ihre x-Position ist die von Zellen mit festen Höhen.

Messed Table Cells http://jamsoftonline.com/images/messed_table_cells.png

Beachten Sie, wie die untere Zelle die richtige Größe hat - es ist überlappend nur die vorherige Zelle, und die vorherige Zelle überlappt seine frühere, und so weiter und so fort.

Bei der Verwendung dieser Methode treten beim Scrollen Artefakte auf, von denen ich denke, dass sie mit der Wiederverwendungskennung für die Zellen zusammenhängen.

So jede Hilfe hier wäre dankbar geschätzt.

+0

Ich habe das gleiche Problem. -cellForRowAtIndexPath: calls -tableView: heightForRowAtIndexPath: wenn es in letzterem aufgerufen wird (beachte, dass es das letztere nicht anruft, wenn es nicht in letzterem aufgerufen wird), mache mich verrückt. – an0

Antwort

38

Hier ist, was ich benutze. NSString verfügt über eine Methode, mit der Sie die Abmessungen eines Textfelds basierend auf den Zeichensatzinformationen und den von Ihnen angegebenen Höhen-/Breiteneinschränkungen angeben können.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *text = [self getTextForIndexPath:indexPath]; 
    UIFont *font = [UIFont systemFontOfSize:14]; 
    CGSize size = [self getSizeOfText:text withFont:font]; 

    return (size.height + 11); // I put some padding on it. 
} 

Dann schreiben Sie eine Methode, um den Text für diese Zelle ziehen ...

- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *sectionHeader = [self.tableSections objectAtIndex:[indexPath section]]; 
    NSString *sectionContent = [self.tableData objectForKey:sectionHeader]; 

    return sectionContent; 
} 

Und das ist die Größe des Textes zu erhalten.

- (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font 
{ 
    return [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 500)]; 
} 
+0

Wer hat das abgelehnt und warum? – Jasarien

+1

Wie kann ich diese Methode nur für eine Zelle in meiner gruppierten Tabellenansicht verwenden? Ich möchte die Höhe der Zelle basierend auf der Höhe eines TextView in der Zelle zurückgeben ... – matteodv

+0

Hallo. Ich möchte wissen, was die 'self.tableSections' und' self.tableData' sind. Ich weiß, dass sie NSArray und NSDictionary sind, aber bedeutet das, dass Sie ein Array von Tabellenabschnitten und ein Wörterbuch für den Inhalt dieser Abschnitte erstellt haben? Ich frage, weil ich nur eine Sektion benutze und ich kann es nicht funktionieren lassen. –

2

Nur ein Gedanke:

  • Was passiert, wenn Sie hatte, sagen wir, sechs verschiedene Arten von Zellen mit jeweils ihre eigene Kennung und einer festen Höhe. Man würde für eine einzeilige Zelle sein, die andere für eine zweizeilige Zelle, etc ...

  • Jedes Mal, wenn Ihr Modell ändert, berechnen die Höhe für diese Reihe dann an den nächsten Zelltyp finden, die Höhe hat am nächsten zu was du brauchst. Speichern Sie diese Zelltypkennung mit dem Modell. Sie können auch die feste Zeilenhöhe für diese Zelle im Modell speichern, damit Sie sie in der tabellarischen Ansicht zurückgeben können: heightForRowAtIndexPath-Aufruf (ich würde nicht zu sehr darauf warten, dass er innerhalb der Zelle selbst berechnet - technisch gesehen ist es kein Teil der Zellzeichnungsfunktionalität und mehr, was die Tabellenansicht verwendet, um zu entscheiden, welcher Zelltyp erstellt werden soll.

  • Wenn Sie aufgefordert werden, eine Zelle für diese Zeile zurückzugeben, müssen Sie lediglich eine Zelle mit dem Zelltyp-Bezeichner erstellen (oder aus dem Zellen-Cache abrufen), die Werte laden und loslegen.

Wenn die Zelle Höhenberechnung zu langsam ist, dann könnte man den gleichen Trick die Tableview Cache tut ziehen und tut es nur auf Abruf, wenn die Zelle in Sicht kommt. Zu einem bestimmten Zeitpunkt müssten Sie dies nur für die Zellen in der Ansicht tun, und dann nur für eine einzelne Zelle, wenn sie an beiden Enden in die Ansicht scrollt.

+0

Ich habe mit dieser Idee in der Vergangenheit gespielt und konnte es nicht genau das tun, was ich wollte. Aber vielleicht kann ich es noch einmal versuchen. – Jasarien

+0

Eine gute und lustige Art, es falsch zu machen –

0

Ich weiß, das wird nicht für Sie arbeiten aufgrund der Endlosschleife Sie erwähnen, aber ich habe einen gewissen Erfolg hatte mit den Zellen layoutSubviews Methode aufrufen

Obwohl dies ein wenig ineffizient sein kann aufgrund mehrere Aufrufe an sowohl CellForRowAtIndexPath und LayoutSubViews, finde ich den Code ist sauberer.

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    MyCell *cell = (MyCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; 

    [cell layoutSubviews]; 

    return CGRectGetHeight(cell.frame); 
} 

Und in dem Layout-Code:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    //First expand the label to a large height to so sizeToFit isn't constrained 
    [self.myArbitrarilyLengthLabel setFrame:CGRectMake(self.myArbitrarilyLengthLabel.frame.origin.x, 
              self.myArbitrarilyLengthLabel.frame.origin.y, 
              self.myArbitrarilyLengthLabel.frame.size.width, 
              1000)]; 


    //let sizeToFit do its magic 
    [self.myArbitrarilyLengthLabel sizeToFit]; 

    //resize the cell to encompass the newly expanded label 
    [self setFrame:CGRectMake(self.frame.origin.x, 
          self.frame.origin.y, 
          self.frame.size.width, 
           CGRectGetMaxY(self.myArbitrarilyLengthLabel.frame) + 10)]; 
} 
Verwandte Themen