tag Wie kann ich Tabellenzellen mit variabler Höhe auf dem iPhone richtig machen?




title tag (3)

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).

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

Meine aktuelle Lösung:

Bevor die Tabellenzellen gerendert werden, berechne ich, wie hoch jede Zelle sein muss, indem ich -sizeWithFont:constrainedToSize: wie -sizeWithFont:constrainedToSize: auf ihren Daten -sizeWithFont:constrainedToSize: . Ich addiere dann die Höhen, erlaube eine Auffüllung und speichere das Ergebnis mit den Daten.

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

Wie gesagt, das funktioniert, aber Aufruf von -sizeWithFont:constrainedToSize: ist sehr langsam, wenn Sie es für hunderte von Elementen sequentiell tun, und ich denke, 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 sich etwas an dem Modellgegenstand änderte, musste ich beide Code-Stücke aktualisieren, und hin und wieder passen sie immer noch nicht perfekt zusammen, was manchmal zu Tabellenzellen führt, die für ein bestimmtes Objekt oder auch etwas zu klein sind groß.

Meine vorgeschlagene Lösung:

Ich möchte also die Zellhöhe vorberechnen. 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 habe versucht, -cellForRowAtIndexPath: von -tableView:heightForRowAtIndexPath: aber das bleibt in einer Endlosschleife -tableView:heightForRowAtIndexPath: , da die erste die Sekunde an einem bestimmten Punkt aufruft und umgekehrt (zumindest habe ich das gesehen, als ich es ausprobiert habe).

Diese Option kommt also nicht in Frage.

Wenn ich in der Höhe für die Zeilendelegiertenmethode keine Größe anwähle, wird die Tabellenansicht verschachtelt. Die Zellen sind die perfekte Höhe, aber ihre x-Position ist die von Zellen mit festen Höhen.

Vermessene Tabellenzellen http://jamsoftonline.com/images/messed_table_cells.png

Beachten Sie, wie die untere Zelle die richtige Größe hat - sie überlappt nur die vorherige Zelle, und die vorherige Zelle überlappt ihre vorherige und so weiter und so fort.

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

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


Nur ein Gedanke:

  • Was wäre, wenn Sie, sagen wir mal, sechs verschiedene Arten von Zellen mit jeweils eigenem Identifikator und fester Höhe hätten? Einer wäre für eine einzeilige Zelle, der andere für eine zweizeilige Zelle, etc ...

  • Jedes Mal, wenn sich Ihr Modell ändert, berechnen Sie die Höhe für diese Zeile und suchen Sie dann den nächsten Zellentyp, dessen Höhe der von Ihnen benötigten Höhe am nächsten kommt. 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 lange darauf warten, dass er innerhalb der Zelle selbst berechnet wird - technisch gesehen ist es kein Teil der Zellenzeichnungsfunktionalität und mehr, was die Tabellenansicht verwendet, um zu entscheiden, welcher Zelltyp erstellt werden soll.

  • Wenn Sie zur Laufzeit 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 Sie können loslegen.

Wenn die Berechnung der Zellenhöhe zu langsam ist, können Sie denselben Trick ausführen, den der Tableview-Cache ausführt, und dies nur bei Bedarf tun, wenn die Zelle sichtbar wird. 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.


Ich weiß, dass dies wegen der von Ihnen erwähnten Endlosschleife nicht funktionieren wird, aber ich hatte Erfolg beim Aufruf der Zellen layoutSubViews-Methode

Obwohl dies aufgrund mehrerer Aufrufe von cellForRowAtIndexPath und layoutSubViews ein wenig ineffizient sein kann, finde ich den Code sauberer.

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

    [cell layoutSubviews];

    return CGRectGetHeight(cell.frame);
}

Und im 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)];
}

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 schreibst du eine Methode, um den Text für diese Zelle zu ziehen ...

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

    return sectionContent;
}

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

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






uitableview