Ich habe eine UITableView
mit einem benutzerdefinierten UITableViewCell
in einem Storyboard mit automatischem Layout definiert. Die Zelle hat mehrere mehrzeilige UILabels
.
Das UITableView
scheint die Zellenhöhen richtig zu berechnen, aber für die ersten Zellen ist diese Höhe nicht richtig zwischen den Beschriftungen aufgeteilt. Nach einigem Scrollen funktioniert alles wie erwartet (auch die Zellen, die anfangs falsch waren).
- (void)viewDidLoad {
[super viewDidLoad]
// ...
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"];
// ...
// Set label.text for variable length string.
return cell;
}
Gibt es etwas, das mir möglicherweise fehlt und das dazu führt, dass das automatische Layout seine Arbeit die ersten Male nicht ausführen kann?
Ich habe ein Beispielprojekt erstellt, das dieses Verhalten demonstriert.
ios
uitableview
ios8
autolayout
ios-autolayout
blackp
quelle
quelle
Antworten:
Ich weiß nicht, ob dies klar dokumentiert ist oder nicht, aber das Hinzufügen
[cell layoutIfNeeded]
vor der Rückgabe der Zelle löst Ihr Problem.quelle
-layoutIfNeeded
in der Zelle tätige-awakeFromNib
. Ich würde lieber nur dort anrufen,layoutIfNeeded
wo ich weiß, warum es notwendig ist.Dies funktionierte für mich, wenn andere ähnliche Lösungen nicht funktionierten:
Dies scheint ein tatsächlicher Fehler zu sein, da ich mit AutoLayout und der Verwendung von UITableViewAutomaticDimension sehr vertraut bin, aber gelegentlich immer noch auf dieses Problem stoße. Ich bin froh, dass ich endlich etwas gefunden habe, das als Problemumgehung funktioniert.
quelle
super.didMoveToSuperview()
didMoveToSuperview
: "Die Standardimplementierung dieser Methode bewirkt nichts."Hinzufügen
[cell layoutIfNeeded]
incellForRowAtIndexPath
funktioniert nicht für Zellen , die zunächst gescrollt out-of-View sind.Es wird auch nicht vorangestellt
[cell setNeedsLayout]
.Sie müssen immer noch bestimmte Zellen aus und wieder in die Ansicht scrollen, damit sie die Größe korrekt ändern.
Dies ist ziemlich frustrierend, da bei den meisten Entwicklern Dynamic Type-, AutoLayout- und Self-Sizing-Zellen ordnungsgemäß funktionieren - mit Ausnahme dieses ärgerlichen Falls. Dieser Fehler betrifft alle meine "größeren" Controller für Tabellenansichten.
quelle
[cell layoutSubviews]
anstelle von layoutIfNeeded kann möglicherweise behoben werden. Siehe stackoverflow.com/a/33515872/1474113Ich hatte die gleiche Erfahrung in einem meiner Projekte.
Warum passiert es?
In Storyboard entworfene Zelle mit einer gewissen Breite für ein Gerät. Zum Beispiel 400px. Zum Beispiel hat Ihr Etikett die gleiche Breite. Wenn es vom Storyboard geladen wird, hat es eine Breite von 400px.
Hier ist ein Problem:
tableView:heightForRowAtIndexPath:
Vor dem Zellenlayout werden die Unteransichten aufgerufen.Also berechnete es die Höhe für Etikett und Zelle mit einer Breite von 400px. Sie laufen jedoch auf einem Gerät mit Bildschirm, z. B. 320px. Und diese automatisch berechnete Höhe ist falsch. Nur weil Zellen
layoutSubviews
erst nachher passieren , hilft es nicht,tableView:heightForRowAtIndexPath:
auch wenn SiepreferredMaxLayoutWidth
Ihr Etikett manuell eingestellt habenlayoutSubviews
.Meine Lösung:
1) Unterklasse
UITableView
und ÜberschreibendequeueReusableCellWithIdentifier:forIndexPath:
. Stellen Sie die Zellenbreite gleich der Tabellenbreite ein und erzwingen Sie das Layout der Zelle.2) Unterklasse
UITableViewCell
. Stellen SiepreferredMaxLayoutWidth
manuell für Ihre Etiketten einlayoutSubviews
. Außerdem benötigen Sie ein manuelles LayoutcontentView
, da das Layout nach dem Ändern des Zellenrahmens nicht automatisch erfolgt (ich weiß nicht warum, aber es ist so).quelle
cell.frame.size.width = tableview.frame.width
danncell.layoutIfNeeded()
in dercellForRowAt
Funktion den Trick für mich gemachtIch habe ein ähnliches Problem, beim ersten Laden wurde die Zeilenhöhe nicht berechnet, aber nach einigem Scrollen oder Wechseln zu einem anderen Bildschirm und ich komme zurück zu diesem Bildschirm Zeilen werden berechnet. Beim ersten Laden werden meine Artikel aus dem Internet geladen und beim zweiten Laden werden meine Artikel zuerst aus Core Data geladen und aus dem Internet neu geladen. Ich habe festgestellt, dass die Zeilenhöhe beim Neuladen aus dem Internet berechnet wird. Daher ist mir aufgefallen, dass beim Aufrufen von tableView.reloadData () während der Segue-Animation (dasselbe Problem mit Push und vorhandenem Segue) die Zeilenhöhe nicht berechnet wurde. Also habe ich die Tabellenansicht bei der Ansichtsinitialisierung ausgeblendet und einen Aktivitätslader eingefügt, um einen hässlichen Effekt für den Benutzer zu verhindern. Nach 300 ms rufe ich tableView.reloadData auf, und jetzt ist das Problem gelöst. Ich denke, es ist ein UIKit-Fehler, aber diese Problemumgehung macht den Trick.
Ich füge diese Zeilen (Swift 3.0) in meinen Handler zum Abschluss des Ladevorgangs ein
Dies erklärt, warum für einige Leute reloadData in layoutSubviews das Problem lösen
quelle
Keine der oben genannten Lösungen hat bei mir funktioniert. Was funktioniert hat, ist dieses Rezept einer Magie: Nennen Sie sie in dieser Reihenfolge:
tableView.reloadData()
tableView.layoutIfNeeded() tableView.beginUpdates() tableView.endUpdates()
Meine tableView-Daten werden von einem Webdienst ausgefüllt. Beim Rückruf der Verbindung schreibe ich die obigen Zeilen.
quelle
In meinem Fall wurde die letzte Zeile des UILabels abgeschnitten, als die Zelle zum ersten Mal angezeigt wurde. Es passierte ziemlich zufällig und die einzige Möglichkeit, die richtige Größe zu bestimmen, bestand darin, die Zelle aus der Ansicht zu scrollen und sie zurückzubringen. Ich habe alle möglichen Lösungen ausprobiert, die bisher angezeigt wurden (layoutIfNeeded..reloadData), aber nichts hat bei mir funktioniert. Der Trick bestand darin, "Autoshrink" auf Minimuum Font Scale (0,5 für mich) einzustellen . Versuche es
quelle
Fügen Sie eine Einschränkung für den gesamten Inhalt einer benutzerdefinierten Zelle für die Tabellenansicht hinzu, schätzen Sie dann die Zeilenhöhe der Tabellenansicht und setzen Sie die Zeilenhöhe auf die automatische Dimension mit einer Ansicht, die geladen wurde:
Um dieses anfängliche Ladeproblem zu beheben, wenden Sie die layoutIfNeeded-Methode mit in einer benutzerdefinierten Tabellenansichtszelle an:
quelle
estimatedRowHeight
einen Wert> 0 und keinen WertUITableViewAutomaticDimension
(-1) festzulegen, da sonst die automatische Zeilenhöhe nicht funktioniert.Ich habe die meisten Antworten auf diese Frage ausprobiert und konnte keine davon zum Laufen bringen. Die einzige funktionale Lösung, die ich gefunden habe, bestand darin, meiner
UITableViewController
Unterklasse Folgendes hinzuzufügen :Der
UIView.performWithoutAnimation
Aufruf ist erforderlich, andernfalls wird beim Laden des View Controllers die normale Tabellenansichtsanimation angezeigt.quelle
viewWillAppear
mir nicht funktioniert, aber es hatviewDidAppear
funktioniert.Einstellung
preferredMaxLayoutWidth
hilft in meinem Fall. Ich fügte hinzuin meinem Code.
Siehe auch Einzeiliger Text nimmt zwei Linien in UILabel und http://openradar.appspot.com/17799811 .
quelle
Das Anrufen von
cell.layoutIfNeeded()
InsidecellForRowAt
funktionierte für mich auf iOS 10 und iOS 11, aber nicht auf iOS 9.Um diese Arbeit auch auf iOS 9 zu bekommen, rufe ich an
cell.layoutSubviews()
und es hat den Trick gemacht.quelle
Für mich funktionierte keiner dieser Ansätze, aber ich stellte fest, dass das Label
Preferred Width
in Interface Builder einen expliziten Satz hatte. Das Entfernen (Deaktivieren von "Explicit") und die anschließende VerwendungUITableViewAutomaticDimension
funktionierten wie erwartet.quelle
Ich habe alle Lösungen auf dieser Seite ausprobiert, aber das Deaktivieren der Option "Größenklassen verwenden" und das erneute Überprüfen haben mein Problem behoben.
Bearbeiten: Das Deaktivieren von Größenklassen verursacht viele Probleme im Storyboard, daher habe ich eine andere Lösung ausprobiert. Ich habe meine Tabellenansicht in den View-Controllern
viewDidLoad
undviewWillAppear
-Methoden ausgefüllt. Dies löste mein Problem.quelle
Ich habe das Problem mit der Größenänderung des Etiketts, daher muss ich
nach dem Einrichten des Textes nur chatTextLabel.text = chatMessage.message chatTextLabel? .UpdateConstraints () ausführen
// vollständiger Code
quelle
In meinem Fall habe ich in einem anderen Zyklus aktualisiert. Daher wurde die Höhe von tableViewCell aktualisiert, nachdem labelText festgelegt wurde. Ich habe den asynchronen Block gelöscht.
quelle
Stellen Sie nur sicher, dass Sie den Beschriftungstext nicht in der Delegierungsmethode 'willdisplaycell' der Tabellenansicht festlegen. Legen Sie den Beschriftungstext in der Delegatenmethode 'cellForRowAtindexPath' für die dynamische Höhenberechnung fest.
Bitte :)
quelle
In meinem Fall verursachte eine Stapelansicht in der Zelle das Problem. Es ist anscheinend ein Fehler. Nachdem ich es entfernt hatte, war das Problem gelöst.
quelle
Das Problem ist, dass die anfänglichen Zellen geladen werden, bevor wir eine gültige Zeilenhöhe haben. Die Problemumgehung besteht darin, ein erneutes Laden der Tabelle zu erzwingen, wenn die Ansicht angezeigt wird.
quelle
Nur für iOS 12+ ab 2019 ...
Ein fortlaufendes Beispiel für Apples gelegentliche bizarre Inkompetenz, bei der Probleme buchstäblich Jahre andauern.
Es scheint so zu sein
wird es beheben. (Sie verlieren natürlich etwas an Leistung.)
So ist das Leben mit Apple.
quelle
In meinem Fall tritt das Problem mit der Zellenhöhe auf, nachdem die anfängliche Tabellenansicht geladen wurde und eine Benutzeraktion stattfindet (Tippen auf eine Schaltfläche in einer Zelle, die die Zellenhöhe ändert). Ich konnte die Zelle nicht dazu bringen, ihre Höhe zu ändern, es sei denn:
Ich habe es versucht
aber das hat nicht funktioniert.
quelle
In Swift 3 musste ich jedes Mal self.layoutIfNeeded () aufrufen, wenn ich den Text der wiederverwendbaren Zelle aktualisiere.
quelle
Keine der oben genannten Lösungen hat funktioniert, aber die folgende Kombination der Vorschläge hat funktioniert.
Musste folgendes in viewDidLoad () hinzufügen.
Die obige Kombination aus reloadData, setNeedsLayout und layoutIfNeeded hat funktioniert, aber keine andere. Könnte jedoch spezifisch für die Zellen im Projekt sein. Und ja, musste reloadData zweimal aufrufen, damit es funktioniert.
Stellen Sie außerdem Folgendes in viewDidLoad ein
In tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
quelle
reloadData/beginUpdates/endUpdates/reloadData
funktioniert auch;reloadData
muss ein zweites Mal aufgerufen werden. Sie müssen es nicht einwickelnasync
.Ich bin auf dieses Problem gestoßen und habe es behoben, indem ich meinen Ansichts- / Etiketteninitialisierungscode von
tableView(willDisplay cell:)
TO nach verschoben habetableView(cellForRowAt:)
.quelle
willDisplay
wird eine bessere Leistung haben alscellForRowAt
. Verwenden Sie nur die letzte, um die richtige Zelle zu instanziieren.willDisplay
die Leistung besser ist, wird empfohlen, die Benutzeroberfläche Ihrer Zelle zu initialisieren,cellForRowAt
wenn das Layout automatisch ist. In der Tat wird das Layout von UIKit nach cellForRowAt et vor willDisplay berechnet. Wenn Ihre Zellenhöhe von ihrem Inhalt abhängt, initialisieren Sie den Etiketteninhalt (oder was auch immer) incellForRowAt
.Verwenden Sie die obige Methode, die die Höhe für die Zeile dynamisch zurückgibt. Weisen Sie dem verwendeten Etikett dieselbe dynamische Höhe zu.
Mit diesem Code können Sie die dynamische Höhe für die Anzeige von Text auf dem Etikett ermitteln.
quelle