Wie verwenden Sie das automatische Layout in UITableViewCell
s in einer Tabellenansicht, damit der Inhalt und die Unteransichten jeder Zelle die Zeilenhöhe (selbst / automatisch) bestimmen und gleichzeitig eine reibungslose Bildlaufleistung gewährleisten?
ios
uitableview
autolayout
nsautolayout
row-height
Smileyborg
quelle
quelle
contentSize
und wiepreferredMaxLayoutWidth
. Siehe hier . Wenn Sie jedoch Ihre Einschränkungen korrekt einrichten, sollten Sie diese nicht benötigenpreferredMaxLayoutWidth
und können in der Tat zu unerwarteten Ergebnissen führen.Antworten:
TL; DR: Lesen Sie nicht gern? Wechseln Sie direkt zu den Beispielprojekten auf GitHub:
Konzeptionelle Beschreibung
Die ersten beiden Schritte gelten unabhängig davon, für welche iOS-Versionen Sie entwickeln.
1. Einrichten und Hinzufügen von Einschränkungen
UITableViewCell
Fügen Sie in Ihrer Unterklasse Einschränkungen hinzu, sodass die Kanten der Unteransichten der Zelle an den Rändern der Inhaltsansicht der Zelle befestigt sind (am wichtigsten an den oberen UND unteren Rändern). HINWEIS: Pin-Unteransichten nicht an die Zelle selbst anheften. nur zu den ZellencontentView
! Lassen Sie die intrinsische Inhaltsgröße dieser Unteransichten die Höhe der Inhaltsansicht der Tabellenansichtszelle bestimmen, indem Sie sicherstellen, dass der Inhaltskomprimierungswiderstand und die inhaltlichen Einschränkungen in der vertikalen Dimension für jede Unteransicht nicht durch von Ihnen hinzugefügte Einschränkungen mit höherer Priorität überschrieben werden. ( Huh? Hier klicken. )Denken Sie daran, dass die Unteransichten der Zelle vertikal mit der Inhaltsansicht der Zelle verbunden werden sollen, damit sie "Druck ausüben" und die Inhaltsansicht entsprechend erweitern können. Anhand einer Beispielzelle mit einigen Unteransichten sehen Sie hier visuell, wie einige (nicht alle!) Ihrer Einschränkungen aussehen müssten:
Sie können sich vorstellen, dass beim Hinzufügen von mehr Text zur mehrzeiligen Körperbezeichnung in der obigen Beispielzelle diese vertikal wachsen muss, um dem Text zu entsprechen, wodurch die Zelle effektiv an Höhe zunimmt. (Natürlich müssen Sie die Einschränkungen richtig einstellen, damit dies richtig funktioniert!)
Die richtigen Einschränkungen sind definitiv der schwierigste und wichtigste Teil , um dynamische Zellenhöhen mit Auto Layout zu erreichen. Wenn Sie hier einen Fehler machen, kann dies dazu führen, dass alles andere nicht mehr funktioniert - nehmen Sie sich also Zeit! Ich empfehle, Ihre Einschränkungen im Code einzurichten, da Sie genau wissen, welche Einschränkungen wo hinzugefügt werden, und es viel einfacher ist, Fehler zu beheben, wenn etwas schief geht. Das Hinzufügen von Einschränkungen im Code kann genauso einfach und wesentlich leistungsfähiger sein als Interface Builder mit Layout-Ankern oder einer der fantastischen Open Source-APIs, die auf GitHub verfügbar sind.
updateConstraints
Methode Ihrer UITableViewCell-Unterklasse tun. Beachten Sie, dass diesupdateConstraints
möglicherweise mehrmals aufgerufen wird. Um zu vermeiden, dass dieselben Einschränkungen mehrmals hinzugefügt werden, sollten Sie Ihren Code zum Hinzufügen von EinschränkungenupdateConstraints
in eine Prüfung auf eine boolesche Eigenschaft wiedidSetupConstraints
(die Sie nach dem Ausführen Ihrer Einschränkung auf YES setzen) einschließen - Code einmal hinzufügen). Wenn Sie jedoch Code haben, der vorhandene Einschränkungen aktualisiert (z. B. das Anpassen derconstant
Eigenschaft für einige Einschränkungen), platzieren Sie diesen in,updateConstraints
aber außerhalb der Prüfung,didSetupConstraints
damit er bei jedem Aufruf der Methode ausgeführt werden kann.2. Bestimmen Sie eindeutige Kennungen für die Wiederverwendung von Zellen in der Tabellenansicht
Verwenden Sie für jeden eindeutigen Satz von Einschränkungen in der Zelle eine eindeutige Kennung für die Wiederverwendung von Zellen. Mit anderen Worten, wenn Ihre Zellen mehr als ein eindeutiges Layout haben, sollte jedes eindeutige Layout eine eigene Wiederverwendungskennung erhalten. (Ein guter Hinweis, dass Sie eine neue Wiederverwendungskennung verwenden müssen, ist, wenn Ihre Zellenvariante eine andere Anzahl von Unteransichten aufweist oder die Unteransichten unterschiedlich angeordnet sind.)
Wenn Sie beispielsweise in jeder Zelle eine E-Mail-Nachricht anzeigen, haben Sie möglicherweise 4 eindeutige Layouts: Nachrichten mit nur einem Betreff, Nachrichten mit einem Betreff und einem Textkörper, Nachrichten mit einem Betreff und einem Fotoanhang sowie Nachrichten mit einem Betreff. Körper und Fotoaufsatz. Für jedes Layout sind völlig andere Einschränkungen erforderlich. Sobald die Zelle initialisiert und die Einschränkungen für einen dieser Zelltypen hinzugefügt wurden, sollte die Zelle eine eindeutige Wiederverwendungskennung erhalten, die für diesen Zelltyp spezifisch ist. Das heißt, wenn Sie eine Zelle zur Wiederverwendung aus der Warteschlange entfernen, wurden die Einschränkungen bereits hinzugefügt und können für diesen Zelltyp verwendet werden.
Beachten Sie, dass Zellen mit denselben Einschränkungen (Typ) aufgrund der unterschiedlichen Größe des Inhalts möglicherweise immer noch unterschiedliche Höhen haben! Verwechseln Sie nicht grundlegend unterschiedliche Layouts (unterschiedliche Einschränkungen) mit unterschiedlichen berechneten Ansichtsrahmen (gelöst aus identischen Einschränkungen) aufgrund unterschiedlicher Inhaltsgrößen.
Für iOS 8 - Zellen mit Selbstgröße
3. Aktivieren Sie die Zeilenhöhenschätzung
Mit iOS 8 hat Apple einen Großteil der Arbeit verinnerlicht, die zuvor von Ihnen vor iOS 8 implementiert werden musste. Damit der Mechanismus für Zellen mit Selbstgröße funktioniert, müssen Sie zuerst die
rowHeight
Eigenschaft in der Tabellenansicht auf die Konstante setzenUITableViewAutomaticDimension
. Anschließend müssen Sie lediglich die Zeilenhöhenschätzung aktivieren, indem Sie dieestimatedRowHeight
Eigenschaft der Tabellenansicht auf einen Wert ungleich Null setzen. Beispiel:Dadurch wird der Tabellenansicht eine temporäre Schätzung / ein Platzhalter für die Zeilenhöhen von Zellen bereitgestellt, die noch nicht auf dem Bildschirm angezeigt werden. Wenn diese Zellen auf dem Bildschirm gescrollt werden, wird die tatsächliche Zeilenhöhe berechnet. Um die tatsächliche Höhe für jede Zeile zu bestimmen, fragt die Tabellenansicht jede Zelle automatisch, welche Höhe sie
contentView
benötigt, basierend auf der bekannten festen Breite der Inhaltsansicht (die auf der Breite der Tabellenansicht basiert, abzüglich zusätzlicher Elemente wie eines Abschnittsindex) oder Zubehöransicht) und die Einschränkungen für das automatische Layout, die Sie der Inhaltsansicht und den Unteransichten der Zelle hinzugefügt haben. Sobald diese tatsächliche Zellenhöhe ermittelt wurde, wird die alte geschätzte Höhe für die Zeile mit der neuen tatsächlichen Höhe aktualisiert (und alle Anpassungen an contentSize / contentOffset der Tabellenansicht werden nach Bedarf für Sie vorgenommen).Im Allgemeinen muss die von Ihnen angegebene Schätzung nicht sehr genau sein. Sie wird nur verwendet, um die Größe der Bildlaufanzeige in der Tabellenansicht korrekt zu bestimmen, und die Tabellenansicht passt die Bildlaufanzeige gut an, wenn Sie falsche Schätzungen vornehmen Bildlaufzellen auf dem Bildschirm. Sie sollten die
estimatedRowHeight
Eigenschaft in der Tabellenansicht (inviewDidLoad
oder ähnlich) auf einen konstanten Wert setzen, der der "durchschnittlichen" Zeilenhöhe entspricht. Nur wenn Ihre Zeilenhöhen extrem variabel sind (z. B. um eine Größenordnung unterschiedlich sind) und Sie beim Scrollen die Bildlaufanzeige "springen" bemerken, sollten Sie sich die MühetableView:estimatedHeightForRowAtIndexPath:
machen, die minimale Berechnung durchzuführen , die erforderlich ist, um eine genauere Schätzung für jede Zeile zurückzugeben.Für iOS 7-Unterstützung (Implementierung der automatischen Zellengröße selbst)
3. Führen Sie einen Layout-Pass durch und ermitteln Sie die Zellenhöhe
Instanziieren Sie zunächst eine Offscreen-Instanz einer Tabellenansichtszelle, eine Instanz für jede Wiederverwendungskennung , die ausschließlich für Höhenberechnungen verwendet wird. (Offscreen bedeutet, dass die Zellreferenz in einer Eigenschaft / ivar auf dem Ansichtscontroller gespeichert und nie zurückgegeben wird,
tableView:cellForRowAtIndexPath:
damit die Tabellenansicht tatsächlich auf dem Bildschirm gerendert wird.) Als Nächstes muss die Zelle mit dem genauen Inhalt (z. B. Text, Bilder usw.) konfiguriert werden. dass es gelten würde, wenn es in der Tabellenansicht angezeigt würde.Erzwingen Sie dann, dass die Zelle ihre Unteransichten sofort anordnet, und verwenden Sie dann die
systemLayoutSizeFittingSize:
Methode auf denUITableViewCell
'scontentView
, um herauszufinden, wie hoch die erforderliche Höhe der Zelle ist. Verwenden SieUILayoutFittingCompressedSize
diese Option , um die kleinste Größe zu erhalten, die für den gesamten Inhalt der Zelle erforderlich ist. Die Höhe kann dann von dertableView:heightForRowAtIndexPath:
Delegatmethode zurückgegeben werden.4. Verwenden Sie Geschätzte Zeilenhöhen
Wenn Ihre Tabellenansicht mehr als ein paar Dutzend Zeilen enthält, werden Sie feststellen, dass das Lösen von Auto-Layout-Einschränkungen beim ersten Laden der Tabellenansicht den Hauptthread schnell blockieren kann, wie dies
tableView:heightForRowAtIndexPath:
für jede einzelne Zeile beim ersten Laden aufgerufen wird ( um die Größe der Bildlaufanzeige zu berechnen).Ab iOS 7 können (und sollten) Sie die
estimatedRowHeight
Eigenschaft in der Tabellenansicht verwenden. Dadurch wird der Tabellenansicht eine temporäre Schätzung / ein Platzhalter für die Zeilenhöhen von Zellen bereitgestellt, die noch nicht auf dem Bildschirm angezeigt werden. Wenn diese Zellen auf dem Bildschirm gescrollt werden, wird die tatsächliche Zeilenhöhe berechnet (durch AufrufentableView:heightForRowAtIndexPath:
) und die geschätzte Höhe mit der tatsächlichen aktualisiert.Im Allgemeinen muss die von Ihnen angegebene Schätzung nicht sehr genau sein. Sie wird nur verwendet, um die Größe der Bildlaufanzeige in der Tabellenansicht korrekt zu bestimmen, und die Tabellenansicht passt die Bildlaufanzeige gut an, wenn Sie falsche Schätzungen vornehmen Bildlaufzellen auf dem Bildschirm. Sie sollten die
estimatedRowHeight
Eigenschaft in der Tabellenansicht (inviewDidLoad
oder ähnlich) auf einen konstanten Wert setzen, der der "durchschnittlichen" Zeilenhöhe entspricht. Nur wenn Ihre Zeilenhöhen extrem variabel sind (z. B. um eine Größenordnung unterschiedlich sind) und Sie beim Scrollen die Bildlaufanzeige "springen" bemerken, sollten Sie sich die MühetableView:estimatedHeightForRowAtIndexPath:
machen, die minimale Berechnung durchzuführen , die erforderlich ist, um eine genauere Schätzung für jede Zeile zurückzugeben.5. (Falls erforderlich) Zeilenhöhen-Caching hinzufügen
Wenn Sie alle oben genannten Schritte ausgeführt haben und immer noch feststellen, dass die Leistung beim Lösen von Einschränkungen inakzeptabel langsam ist
tableView:heightForRowAtIndexPath:
, müssen Sie leider ein gewisses Caching für Zellenhöhen implementieren. (Dies ist der von den Apple-Ingenieuren vorgeschlagene Ansatz.) Die allgemeine Idee besteht darin, die Autolayout-Engine die Einschränkungen beim ersten Mal lösen zu lassen, dann die berechnete Höhe für diese Zelle zwischenzuspeichern und den zwischengespeicherten Wert für alle zukünftigen Anforderungen für die Höhe dieser Zelle zu verwenden. Der Trick besteht natürlich darin, sicherzustellen, dass Sie die zwischengespeicherte Höhe für eine Zelle löschen, wenn etwas passiert, das dazu führen kann, dass sich die Höhe der Zelle ändert. Dies ist in erster Linie dann der Fall, wenn sich der Inhalt dieser Zelle ändert oder wenn andere wichtige Ereignisse auftreten (z. B. das Anpassen durch den Benutzer) den Schieberegler für die Textgröße "Dynamischer Typ").Generischer Beispielcode für iOS 7 (mit vielen saftigen Kommentaren)
Beispielprojekte
Diese Projekte sind voll funktionsfähige Beispiele für Tabellenansichten mit variablen Zeilenhöhen aufgrund von Tabellenansichtszellen, die dynamischen Inhalt in UILabels enthalten.
Xamarin (C # /. NET)
Wenn Sie Xamarin verwenden, check this out Beispielprojekt zusammengestellt von @KentBoogaart .
quelle
heightForRowAtIndexPath
, indem Sie die Zelle aus der Warteschlange entfernen , sie behalten und beim nächstencellForRowAtIndexPath
Aufruf zurückgeben.iOS8
Implementierung füriOS9
und noch empfohleniOS10
, oder gibt es neue Ansätze, seit diese Antwort veröffentlicht wurde?Für iOS 8 oben ist es wirklich einfach:
oder
Für iOS 7 ist der Schlüssel jedoch die Berechnung der Höhe nach dem automatischen Layout:
Wichtig
Wenn mehrere Zeilen beschriftet sind, vergessen Sie nicht, das auf
numberOfLines
zu setzen0
.Vergiss nicht
label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)
Der vollständige Beispielcode ist hier .
quelle
Schnelles Beispiel für eine UITableViewCell mit variabler Höhe
Aktualisiert für Swift 3
Die schnelle Antwort von William Hu ist gut, aber es hilft mir, einige einfache, aber detaillierte Schritte zu haben, wenn ich zum ersten Mal lerne, etwas zu tun. Das folgende Beispiel ist mein Testprojekt, während ich lerne, ein
UITableView
mit variablen Zellenhöhen zu erstellen. Ich habe es auf diesem grundlegenden UITableView-Beispiel für Swift basiert .Das fertige Projekt sollte folgendermaßen aussehen:
Erstellen Sie ein neues Projekt
Es kann sich nur um eine Single View-Anwendung handeln.
Fügen Sie den Code hinzu
Fügen Sie Ihrem Projekt eine neue Swift-Datei hinzu. Nennen Sie es MyCustomCell. Diese Klasse enthält die Ausgänge für die Ansichten, die Sie Ihrer Zelle im Storyboard hinzufügen. In diesem grundlegenden Beispiel haben wir nur eine Bezeichnung in jeder Zelle.
Wir werden diese Steckdose später anschließen.
Öffnen Sie ViewController.swift und stellen Sie sicher, dass Sie über den folgenden Inhalt verfügen:
Wichtige Notiz:
Es sind die folgenden zwei Codezeilen (zusammen mit dem automatischen Layout), die die variable Zellenhöhe ermöglichen:
Richten Sie das Storyboard ein
Fügen Sie Ihrem Ansichts-Controller eine Tabellenansicht hinzu und heften Sie sie mithilfe des automatischen Layouts an die vier Seiten. Ziehen Sie dann eine Tabellenansichtszelle auf die Tabellenansicht. Ziehen Sie auf die Prototypzelle ein Etikett. Verwenden Sie das automatische Layout, um die Beschriftung an den vier Kanten der Inhaltsansicht der Tabellenansichtszelle zu befestigen.
Wichtige Notiz:
Andere IB-Einstellungen
Benutzerdefinierter Klassenname und Bezeichner
Wählen Sie die Tabellenansichtszelle aus und legen Sie die benutzerdefinierte Klasse fest
MyCustomCell
(den Namen der Klasse in der von uns hinzugefügten Swift-Datei). Setzen Sie auch den Bezeichner aufcell
(dieselbe Zeichenfolge, die wircellReuseIdentifier
im obigen Code für verwendet haben.Nulllinien für Beschriftung
Stellen Sie die Anzahl der Zeilen
0
in Ihrem Etikett ein. Dies bedeutet mehrzeilig und ermöglicht es dem Etikett, seine Größe basierend auf seinem Inhalt zu ändern.Schließen Sie die Steckdosen an
tableView
Variable imViewController
Code.myCellLabel
Variablen in derMyCustomCell
Klasse.Fertig
Sie sollten jetzt in der Lage sein, Ihr Projekt auszuführen und Zellen mit variabler Höhe zu erhalten.
Anmerkungen
Wenn Sie die Vorder- und Hinterkante (links und rechts) nicht fixieren, müssen Sie möglicherweise auch die Beschriftungen
preferredMaxLayoutWidth
so einstellen , dass sie wissen, wann der Zeilenumbruch erfolgen soll. Wenn Sie beispielsweise der Beschriftung im obigen Projekt eine horizontale Einschränkung für die horizontale Mitte hinzugefügt haben, anstatt die Vorder- und Hinterkante zu fixieren, müssen Sie diese Zeile zurtableView:cellForRowAtIndexPath
Methode hinzufügen :Siehe auch
quelle
preferredMaxLayoutWidth
gegen die Zelle zu stellencontentSize
? Auf diese Weise wird eine Zubehöransicht, die zum Bearbeiten überstrichen wurde, trotzdem berücksichtigt?Ich habe die iOS7-Lösung von @ smileyborg in eine Kategorie eingepackt
Ich habe beschlossen, diese clevere Lösung von @smileyborg in eine
UICollectionViewCell+AutoLayoutDynamicHeightCalculation
Kategorie einzuteilen .Die Kategorie behebt auch die in der Antwort von @ wildmonkey beschriebenen Probleme (Laden einer Zelle von einer Feder und
systemLayoutSizeFittingSize:
ZurückkehrenCGRectZero
)Es berücksichtigt kein Caching, entspricht aber gerade meinen Anforderungen. Fühlen Sie sich frei, es zu kopieren, einzufügen und zu hacken.
UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h
UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m
Anwendungsbeispiel:
Zum Glück müssen wir diesen Jazz nicht in iOS8 machen, aber da ist er jetzt!
quelle
[YourCell new]
und dieses als Dummy zu verwenden. Solange der Code zum Erstellen von Einschränkungscodes in Ihrer Instanz ausgelöst wird und Sie programmgesteuert einen Layoutdurchlauf auslösen, sollten Sie bereit sein.UICollectionViews
.Hier ist meine Lösung:
Sie müssen das sagen ,
TableView
die ,estimatedHeight
bevor sie die Ansicht laden. Andernfalls kann es sich nicht wie erwartet verhalten.Ziel c
Update auf Swift 4.2
quelle
estimatedRowHeight
sich Zeile für Zeile ändert? sollte ich über- oder unterschätzen? Minimum oder Maximum der Höhe verwenden, in der ich benutzetableView
?estimatedRowHeight
, es wirkt sich hauptsächlich auf die Größe der Bildlaufleiste aus, niemals auf die Höhe der tatsächlichen Zellen. Seien Sie in der von Ihnen gewählten Höhe fett: Dies wirkt sich auf die Animation zum Einfügen / Löschen aus.Die von @smileyborg vorgeschlagene Lösung ist nahezu perfekt. Wenn Sie eine benutzerdefinierte Zelle haben und eine oder mehrere
UILabel
mit dynamischen Höhen möchten, gibt die systemLayoutSizeFittingSize- Methode in Kombination mit aktiviertem AutoLayout a zurück, esCGSizeZero
sei denn, Sie verschieben alle Ihre Zellenbeschränkungen aus der Zelle in die Inhaltsansicht (wie von @TomSwift hier vorgeschlagen) Alle Unteransichten mit Autolayout ausstatten? ).Dazu müssen Sie den folgenden Code in Ihre benutzerdefinierte UITableViewCell-Implementierung einfügen (dank @Adrian).
Das Mischen der @ smileyborg-Antwort mit dieser sollte funktionieren.
quelle
systemLayoutSizeFittingSize
muss auf contentView aufgerufen werden, nicht auf cellEin wichtiger Punkt, auf den ich gerade gestoßen bin, um ihn als Antwort zu posten.
@ smileyborgs Antwort ist größtenteils richtig. Wenn Sie jedoch Code in der
layoutSubviews
Methode Ihrer benutzerdefinierten Zellenklasse haben, z. B. das Festlegen vonpreferredMaxLayoutWidth
, wird dieser nicht mit diesem Code ausgeführt:Es hat mich eine Weile verwirrt. Dann wurde mir klar, dass diese nur layoutSubviews auf
contentView
der Zelle auslösen , nicht auf der Zelle selbst.Mein Arbeitscode sieht folgendermaßen aus:
Beachten Sie, dass Sie beim Erstellen einer neuen Zelle sicher nicht anrufen müssen,
setNeedsLayout
da diese bereits festgelegt sein sollte. In Fällen, in denen Sie einen Verweis auf eine Zelle speichern, sollten Sie ihn wahrscheinlich aufrufen. In jedem Fall sollte es nichts tun.Ein weiterer Tipp, wenn Sie Zellunterklassen verwenden, in denen Sie beispielsweise Einstellungen vornehmen
preferredMaxLayoutWidth
. Wie @smileyborg erwähnt, "wurde die Breite Ihrer Tabellenansichtszelle noch nicht auf die Breite der Tabellenansicht festgelegt". Dies ist richtig und problematisch, wenn Sie Ihre Arbeit in Ihrer Unterklasse und nicht im View Controller ausführen. Sie können den Zellenrahmen jedoch einfach an dieser Stelle mithilfe der Tabellenbreite festlegen:Zum Beispiel bei der Berechnung der Höhe:
(Ich speichere diese bestimmte Zelle zur Wiederverwendung zwischen, aber das ist irrelevant).
quelle
Für den Fall, dass die Leute immer noch Probleme damit haben. Ich habe einen kurzen Blog-Beitrag über die Verwendung von Autolayout mit UITableViews geschrieben, der Autolayout für dynamische Zellenhöhen nutzt, sowie eine Open-Source-Komponente, um dies abstrakter und einfacher zu implementieren. https://github.com/Raizlabs/RZCellSizeManager
quelle
Solange Ihr Layout in Ihrer Zelle gut ist.
Update: Sie sollten die in iOS 8 eingeführte dynamische Größenänderung verwenden.
quelle
tableView:cellForRowAtIndexPath:
intableView:heightForRowAtIndexPath:
jetzt?systemLayoutSizeFittingSize:
intableView:cellForRowAtIndexPath:
und das Ergebnis dann zwischengespeichert und dann verwenden , intableView:heightForRowAtIndexPath:
es gut , so lange funktioniert , wie die Einschränkungen richtig natürlich Setup sind!(für Xcode 8.x / Xcode 9.x unten lesen)
Beachten Sie das folgende Problem in Xcode 7.x, das Verwirrung stiften kann:
Der Interface Builder behandelt die Einrichtung der Zellen mit automatischer Größenanpassung nicht ordnungsgemäß. Auch wenn Ihre Einschränkungen absolut gültig sind, wird sich IB dennoch beschweren und Ihnen verwirrende Vorschläge und Fehler geben. Der Grund dafür ist, dass IB nicht bereit ist, die Höhe der Zeile gemäß Ihren Einschränkungen zu ändern (sodass die Zelle um Ihren Inhalt herum passt). Stattdessen bleibt die Höhe der Zeile unverändert und es wird vorgeschlagen, dass Sie Ihre Einschränkungen ändern, die Sie ignorieren sollten .
Stellen Sie sich zum Beispiel vor, Sie haben alles in Ordnung eingerichtet, keine Warnungen, keine Fehler, alles funktioniert.
Wenn Sie nun die Schriftgröße ändern (in diesem Beispiel ändere ich die Schriftgröße des Beschreibungsetiketts von 17.0 auf 18.0).
Da die Schriftgröße erhöht wurde, möchte das Etikett jetzt 3 Zeilen belegen (zuvor waren es 2 Zeilen).
Wenn Interface Builder wie erwartet funktioniert, wird die Größe der Zelle an die neue Beschriftungshöhe angepasst. Was jedoch tatsächlich passiert, ist, dass IB das rote Symbol für den Fehler beim automatischen Layout anzeigt und vorschlägt, dass Sie die Prioritäten für das Umarmen / Komprimieren ändern.
Sie sollten diese Warnungen ignorieren. Sie können stattdessen * die Zeilenhöhe manuell ändern (wählen Sie Zelle> Größeninspektor> Zeilenhöhe).
Ich habe diese Höhe klickweise geändert (mit dem Stepper nach oben / unten), bis die Fehler mit dem roten Pfeil verschwunden sind! (Sie erhalten tatsächlich gelbe Warnungen. An diesem Punkt führen Sie einfach "Frames aktualisieren" durch. Es sollte alles funktionieren.)
* Beachten Sie, dass Sie diese roten oder gelben Warnungen im Interface Builder nicht beheben müssen - zur Laufzeit funktioniert alles ordnungsgemäß (auch wenn IB Fehler / Warnungen anzeigt). Stellen Sie einfach sicher, dass zur Laufzeit im Konsolenprotokoll keine AutoLayout-Fehler angezeigt werden.
Tatsächlich ist der Versuch, die Zeilenhöhe in IB immer zu aktualisieren, sehr ärgerlich und manchmal nahezu unmöglich (aufgrund von Bruchwerten).
Um die lästigen IB-Warnungen / Fehler zu vermeiden, können Sie die beteiligten Ansichten auswählen und
Size Inspector
für die EigenschaftAmbiguity
auswählenVerify Position Only
Xcode 8.x / Xcode 9.x scheint (manchmal) Dinge anders zu machen als Xcode 7.x, aber immer noch falsch. Selbst wenn
compression resistance priority
/hugging priority
auf "Erforderlich" (1000) gesetzt ist, kann Interface Builder beispielsweise eine Beschriftung dehnen oder abschneiden, um sie an die Zelle anzupassen (anstatt die Zellenhöhe so zu ändern, dass sie um die Beschriftung herum passt). In einem solchen Fall werden möglicherweise nicht einmal AutoLayout-Warnungen oder -Fehler angezeigt. Oder manchmal macht es genau das, was Xcode 7.x gemacht hat, wie oben beschrieben.quelle
Um die automatische Bemaßung für die Zeilenhöhe und die geschätzte Zeilenhöhe festzulegen, müssen Sie die folgenden Schritte ausführen, damit die automatische Bemaßung für das Layout der Zellen- / Zeilenhöhe wirksam wird.
UITableViewAutomaticDimension
zu rowHeight & geschätzteRowHeightheightForRowAt
geben Sie einen Wert zurückUITableViewAutomaticDimension
)- -
Ziel c:
Schnell:
Für Label-Instanz in UITableviewCell
Hinweis : Wenn Sie mehr als ein Etikett (UIElements) mit dynamischer Länge haben, das entsprechend seiner Inhaltsgröße angepasst werden sollte: Passen Sie die Priorität "Inhaltsumarmung und Kompressionswiderstand" für Etiketten an, die Sie mit höherer Priorität erweitern / komprimieren möchten.
quelle
tableView: heightForRow
Datenquelle.tableView: heightForRow
. (für iOS 10-tableView: heightForRow
ist erforderlich)tableView: heightForRow
..if (indexPath.row == 0) { return 100} else { return UITableView.automaticDimension }
Wie bei @ Bob-Spryn bin ich auf ein so wichtiges Gotcha gestoßen, dass ich dies als Antwort poste .
Ich kämpfte eine Weile mit @ smileyborgs Antwort. Das Problem, auf das ich gestoßen bin, ist, wenn Sie Ihre Prototypzelle in IB mit zusätzlichen Elementen (
UILabels
,UIButtons
usw.) in IB definiert haben, wenn Sie die Zelle mit [[YourTableViewCellClass alloc] init]
instanziieren, werden nicht alle anderen Elemente in dieser Zelle instanziiert, es sei denn, Sie haben geschriebener Code, um das zu tun. (Ich hatte eine ähnliche Erfahrung mitinitWithStyle
.)Damit das Storyboard alle zusätzlichen Elemente instanziiert, erhalten Sie Ihre Zelle mit
[tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"]
(Nicht,[tableView dequeueReusableCellWithIdentifier:forIndexPath:]
da dies interessante Probleme verursacht.) Wenn Sie dies tun, werden alle in IB definierten Elemente instanziiert.quelle
Zellenhöhe in der dynamischen Tabellenansicht und automatisches Layout
Ein guter Weg, um das Problem mit dem automatischen Layout des Storyboards zu lösen:
quelle
quelle
Eine weitere "Lösung": Überspringen Sie all diese Frustrationen und verwenden Sie stattdessen eine UIScrollView, um ein Ergebnis zu erhalten, das mit UITableView identisch aussieht und sich identisch anfühlt.
Das war die schmerzhafte "Lösung" für mich, nachdem ich buchstäblich mehr als 20 sehr frustrierende Stunden damit verbracht hatte, etwas zu bauen, wie es Smileyborg vorgeschlagen hatte, und über viele Monate und drei Versionen von App Store-Versionen gescheitert war.
Meiner Meinung nach ist die Technologie einfach zu spröde, wenn Sie wirklich iOS 7-Unterstützung benötigen (für uns ist dies unerlässlich), und Sie werden sich beim Versuch die Haare aus dem Kopf ziehen. Und dass UITableView im Allgemeinen ein völliger Overkill ist, es sei denn, Sie verwenden einige der erweiterten Funktionen zur Zeilenbearbeitung und / oder müssen wirklich mehr als 1000 "Zeilen" unterstützen (in unserer App sind es realistisch gesehen nie mehr als 20 Zeilen).
Der zusätzliche Bonus ist, dass der Code wahnsinnig einfach wird im Vergleich zu all dem Delegierten-Mist und hin und her, der mit UITableView geliefert wird. Es ist nur eine einzige Codeschleife in viewOnLoad, die elegant aussieht und einfach zu verwalten ist.
Hier sind einige Tipps dazu:
Erstellen Sie mit Storyboard oder einer NIB-Datei einen ViewController und die zugehörige Stammansicht.
Ziehen Sie über eine UIScrollView in Ihre Stammansicht.
Fügen Sie der Ansicht der obersten Ebene Einschränkungen oben, unten, links und rechts hinzu, damit die UIScrollView die gesamte Stammansicht ausfüllt.
Fügen Sie eine UIView in die UIScrollView ein und nennen Sie sie "Container". Fügen Sie der UIScrollView (ihrem übergeordneten Element) obere, untere, linke und rechte Einschränkungen hinzu. KEY TRICK: Fügen Sie außerdem eine Einschränkung "Gleiche Breite" hinzu, um UIScrollView und UIView zu verknüpfen.
HINWEIS: Sie erhalten die Fehlermeldung "Die Bildlaufansicht hat eine mehrdeutige Höhe des scrollbaren Inhalts" und dass Ihre Container-UIView eine Höhe von 0 Pixel haben sollte. Keiner der Fehler scheint von Bedeutung zu sein, wenn die App ausgeführt wird.
Erstellen Sie NIB-Dateien und Controller für jede Ihrer "Zellen". Verwenden Sie UIView, nicht UITableViewCell.
In Ihrem Root-ViewController fügen Sie im Wesentlichen alle "Zeilen" zur Container-UIView hinzu und fügen programmgesteuert Einschränkungen hinzu, die den linken und rechten Rand mit der Containeransicht, die oberen Kanten entweder mit der Containeransicht oben (für das erste Element) oder dem vorherigen verknüpfen Zelle. Verbinden Sie dann die letzte Zelle mit dem Behälterboden.
Für uns befindet sich jede "Zeile" in einer NIB-Datei. Der Code sieht also ungefähr so aus:
Und hier ist der Code für UITools.addViewToTop:
Das einzige "Gotcha", das ich bisher bei diesem Ansatz gefunden habe, ist, dass UITableView eine nette Funktion von "schwebenden" Abschnittsüberschriften oben in der Ansicht hat, während Sie scrollen. Die obige Lösung wird dies nur tun, wenn Sie mehr Programmierung hinzufügen. In unserem speziellen Fall war diese Funktion jedoch nicht zu 100% erforderlich, und niemand bemerkte, wann sie nicht mehr vorhanden war.
Wenn Sie Teiler zwischen Ihren Zellen wünschen, fügen Sie einfach eine 1 Pixel hohe UIView am unteren Rand Ihrer benutzerdefinierten "Zelle" hinzu, die wie ein Teiler aussieht.
Stellen Sie sicher, dass "Bounces" und "Bounce Vertical" aktiviert sind, damit die Aktualisierungssteuerung funktioniert. Es scheint also eher eine Tabellenansicht zu sein.
TableView zeigt einige leere Zeilen und Teiler unter Ihrem Inhalt an, wenn es nicht den gesamten Bildschirm ausfüllt, wie dies bei dieser Lösung nicht der Fall ist. Aber ich persönlich bevorzuge es, wenn diese leeren Zeilen sowieso nicht da wären - bei variabler Zellenhöhe sah es für mich sowieso immer "fehlerhaft" aus, die leeren Zeilen dort zu haben.
Wir hoffen, dass ein anderer Programmierer meinen Beitrag liest, bevor er mehr als 20 Stunden damit verschwendet, ihn mit Table View in seiner eigenen App herauszufinden. :) :)
quelle
Ich musste dynamische Ansichten verwenden (Setup-Ansichten und Einschränkungen nach Code) und als ich die Breite des bevorzugten MaxLayoutWidth-Labels festlegen wollte, war 0. Daher habe ich eine falsche Zellenhöhe.
Dann habe ich hinzugefügt
vor der Ausführung
Danach war die Breite des Etiketts wie erwartet und die dynamische Höhe wurde richtig berechnet.
quelle
Angenommen, Sie haben eine Zelle mit einer Unteransicht und möchten, dass die Höhe der Zelle hoch genug ist, um die Unteransicht + das Auffüllen zu umfassen.
1) Setzen Sie die untere Einschränkung der Unteransicht gleich der cell.contentView abzüglich der gewünschten Auffüllung. Legen Sie keine Einschränkungen für die Zelle oder cell.contentView selbst fest.
2) Setzen Sie entweder die
rowHeight
Eigenschaft tableView odertableView:heightForRowAtIndexPath:
aufUITableViewAutomaticDimension
.3) Legen Sie entweder die
estimatedRowHeight
Eigenschaft von tableView odertableView:estimatedHeightForRowAtIndexPath:
eine bestmögliche Schätzung der Höhe fest.Das ist es.
quelle
Wenn Sie programmgesteuert gestalten, sollten Sie Folgendes für iOS 10 beachten, indem Sie Anker in Swift verwenden.
Es gibt drei Regeln / Schritte
NUMMER 1: Legen Sie diese beiden Eigenschaften der Tabellenansicht in viewDidLoad fest. Die erste teilt der Tabellenansicht mit, dass dynamische Größen für ihre Zellen erwartet werden sollen. Die zweite dient lediglich dazu, die App die Größe des Bildlaufleistenindikators berechnen zu lassen, damit dies hilfreich ist Performance.
NUMMER 2: Dies ist wichtig. Sie müssen die Unteransichten zur Inhaltsansicht der Zelle hinzufügen, nicht zur Ansicht, und die Layoutansichten verwenden, um die Unteransichten oben und unten zu verankern. Dies ist ein funktionierendes Beispiel dafür.
Erstellen Sie eine Methode, die die Unteransichten hinzufügt und das Layout ausführt, und rufen Sie sie in der init-Methode auf.
NUMMER 3: RUFEN SIE DIE METHODE NICHT AN:
Wenn Sie dies tun, überschreiben Sie Ihre Implementierung.
Befolgen Sie diese 3 Regeln für dynamische Zellen in Tabellenansichten.
Hier ist eine funktionierende Implementierung https://github.com/jamesrochabrun/MinimalViewController
quelle
Wenn Sie eine lange Zeichenfolge haben. zB eine, die keinen Zeilenumbruch hat. Dann könnten Sie auf einige Probleme stoßen.
Die "angebliche" Korrektur wird durch die akzeptierte Antwort und einige andere Antworten erwähnt. Sie müssen nur hinzufügen
Ich finde Suraghs Antwort am vollständigsten und prägnantesten , daher nicht verwirrend.
Erklären Sie jedoch nicht, warum diese Änderungen erforderlich sind. Lass uns das tun.
Fügen Sie den folgenden Code in ein Projekt ein.
Beachten Sie, dass ich keine Größenbeschränkungen hinzugefügt habe . Ich habe nur CenterX- und CenterY-Einschränkungen hinzugefügt. Trotzdem wird das Etikett die richtige Größe haben. Warum?
Wegen
contentSize
.Um dies besser zu verarbeiten, behalten Sie zuerst Schritt 0 bei und kommentieren Sie dann die Schritte 1 bis 6 aus. Lass
setupLayout()
bleiben. Beobachten Sie das Verhalten.Dann Kommentar 1 auskommentieren und beobachten.
Dann Kommentar 2 auskommentieren und beobachten.
Tun Sie dies, bis Sie alle 6 Schritte auskommentiert und deren Verhalten beobachtet haben.
Was kann daraus geschlossen werden? Welche Faktoren können das ändern
contenSize
?\n
, entspricht die Breite von intrinsicContentSize der maximalen Breite aller Zeilen. Wenn eine Zeile 25 Zeichen, eine andere 2 Zeichen und eine andere 21 Zeichen enthält, wird Ihre Breite anhand der 25 Zeichen berechnetnumberOfLines
, da0
sonst nicht mehrere Zeilen vorhanden sind. SienumberOfLines
passen die Höhe Ihres intrinsicContentSize anVornehmen von Anpassungen: Stellen Sie sich vor, basierend auf Ihrem Text war die Breite
200
und Höhe Ihres intrinsicContentSize100
, aber Sie wollten die Breite auf den Container des Etiketts beschränken. Was werden Sie tun? Die Lösung besteht darin, die gewünschte Breite einzustellen. Sie tun das , indem SiepreferredMaxLayoutWidth
zu130
Ihrem neuen intrinsicContentSize dann etwa eine Breite von haben130
. Die Höhe wäre offensichtlich größer als100
weil Sie mehr Linien benötigen würden. Davon abgesehen, wenn Ihre Einschränkungen richtig eingestellt sind, müssen Sie dies überhaupt nicht verwenden! Weitere Informationen hierzu finden Sie in dieser Antwort und ihren Kommentaren. Sie müssen nur GebrauchpreferredMaxLayoutWidth
wenn Sie keine Einschränkungen haben, die die Breite / Höhe einschränken, wie in "Man darf den Text nicht umbrechen, es sei denn, er überschreitet die."preferredMaxLayoutWidth
". Aber mit 100% iger Sicherheit, wenn Sie den Leading / Trailing einstellen undnumberOfLines
bis0
dahin sind Sie gut!Lange Rede, kurzer Sinn, die meisten Antworten hier, die die Verwendung empfehlen, sind FALSCH! Du brauchst es nicht. Die Notwendigkeit ist ein Zeichen dafür, dass Ihre Einschränkungen nicht richtig eingestellt sind oder dass Sie einfach keine Einschränkungen habenSchriftgröße: Beachten Sie auch, dass sich die Höhe von intrinsicContentSize erhöht, wenn Sie Ihre Schriftgröße erhöhen. Das habe ich in meinem Code nicht gezeigt. Sie können das selbst versuchen.
Zurück zu Ihrem tableViewCell-Beispiel:
Alles was Sie tun müssen ist:
numberOfLines
auf0
preferredMaxLayoutWidth
.quelle
In meinem Fall muss ich eine benutzerdefinierte Zelle mit einem Bild erstellen, das vom Server stammt und eine beliebige Breite und Höhe haben kann. Und zwei UILabels mit dynamischer Größe (sowohl Breite als auch Höhe)
das habe ich hier in meiner antwort mit autolayout und programmatisch erreicht:
Grundsätzlich hat die Antwort über @smileyBorg geholfen, aber systemLayoutSizeFittingSize hat bei mir nie funktioniert. In meinem Ansatz:
1. Keine Verwendung der Eigenschaft zur automatischen Berechnung der Zeilenhöhe. 2. Keine Verwendung der geschätzten Höhe 3. Keine unnötigen Update-Einschränkungen erforderlich. 4. Keine Verwendung der automatischen bevorzugten maximalen Layoutbreite. 5. Keine Verwendung von systemLayoutSizeFittingSize (sollte verwendet werden, funktioniert aber nicht für mich, ich weiß nicht, was es intern tut), sondern meine Methode - (float) getViewHeight funktioniert und ich weiß, was es intern tut.
Ist es möglich, dass eine UITableView-Zelle unterschiedliche Höhen aufweist, wenn ich die Zelle auf verschiedene Arten anzeige?
quelle
In meinem Fall lag das Auffüllen an den Höhen von sectionHeader und sectionFooter, bei denen ich es im Storyboard auf mindestens 1 ändern konnte. In der viewDidLoad-Methode also:
quelle
Ich habe nur ein paar dumme Versuche mit den 2 Werten von
rowHeight
und gemachtestimatedRowHeight
und dachte nur, es könnte einen Einblick in das Debuggen geben:Wenn Sie beide ODER nur die
estimatedRowHeight
einstellen, erhalten Sie das gewünschte Verhalten:Es wird empfohlen, dass Sie Ihr Bestes geben, um die richtige Schätzung zu erhalten, aber das Endergebnis ist nicht anders. Dies wirkt sich nur auf Ihre Leistung aus.
Wenn Sie nur die rowHeight einstellen, dh nur:
Ihr Endergebnis wäre nicht wie gewünscht:
Wenn Sie den Wert
estimatedRowHeight
auf 1 oder kleiner setzen, stürzen Sie unabhängig vom abrowHeight
.Ich bin mit der folgenden Fehlermeldung abgestürzt:
quelle
In Bezug auf die akzeptierte Antwort von @smileyborg habe ich gefunden
in einigen Fällen unzuverlässig zu sein, in denen Einschränkungen nicht eindeutig sind. Es ist besser, die Layout-Engine zu zwingen, die Höhe in eine Richtung zu berechnen, indem Sie die Hilfskategorie in UIView unten verwenden:
Wobei w: die Breite der Tabellenansicht ist
quelle
Fügen Sie einfach diese beiden Funktionen in Ihren Viewcontroller ein, um Ihr Problem zu lösen. Hier ist list ein String-Array, das den String jeder Zeile enthält.
quelle
quelle
Wenn die Zellenhöhe durch den Inhalt dynamisch ist, sollten Sie sie genau zählen und dann den Höhenwert zurückgeben, bevor die Zelle gerendert wird. Eine einfache Möglichkeit besteht darin, die Zählmethode im Zellencode der Tabellenansicht zu definieren, damit der Controller die Delegatenmethode für die Tabellenzellenhöhe aufruft. Vergessen Sie nicht, die tatsächliche Breite des Zellenrahmens zu zählen (Standard ist 320), wenn die Höhe von der Breite der Tabelle oder des Bildschirms abhängt. Das heißt, verwenden Sie in der Delegierungsmethode für Tabellenzellenhöhen zuerst cell.frame, um die Zellenbreite zu korrigieren, und rufen Sie dann die in der Zelle definierte Zählhöhenmethode auf, um den geeigneten Wert zu erhalten und ihn zurückzugeben .
PS. Der Code zum Generieren eines Zellenobjekts könnte in einer anderen Methode definiert werden, damit eine andere Zelldelegatmethode für Tabellenansichten aufgerufen werden kann.
quelle
UITableView.automaticDimension
kann über Interface Builder eingestellt werden:Xcode> Storyboard> Größeninspektor
Tabellenansicht Zelle> Zeilenhöhe> Automatisch
quelle
eine weitere iOs7 + iOs8-Lösung in Swift
quelle
cellForRowAtIndexPath
, die Zelle ist zu diesem Zeitpunkt noch nicht ausgelegt.