Es hört sich so an, als ob Sie nach einer Möglichkeit suchen, mit UICollectionView ein Layout wie UITableView zu erstellen. Wenn Sie das wirklich möchten, können Sie dies mit einer benutzerdefinierten UICollectionViewLayout-Unterklasse (möglicherweise mit SBTableLayout ) tun .
Auf der anderen Seite, wenn Sie wirklich fragen, ob es einen sauberen Weg gibt, dies mit dem Standard-UICollectionViewFlowLayout zu tun, dann glaube ich, dass es keinen Weg gibt. Selbst mit den selbstformatierenden Zellen von iOS8 ist dies nicht einfach. Wie Sie sagen, besteht das grundlegende Problem darin, dass die Maschinen des Flusslayouts keine Möglichkeit bieten, eine Dimension zu fixieren und eine andere reagieren zu lassen. (Selbst wenn Sie dies könnten, wäre die Notwendigkeit, zwei Layoutdurchläufe für die Größe der mehrzeiligen Beschriftungen zu benötigen, zusätzlich komplex. Dies passt möglicherweise nicht dazu, wie Zellen mit Selbstgröße die gesamte Größe über einen Aufruf von systemLayoutSizeFittingSize berechnen möchten.)
Wenn Sie jedoch weiterhin ein tabellenansichtähnliches Layout mit einem Ablauflayout erstellen möchten, dessen Zellen ihre eigene Größe bestimmen und natürlich auf die Breite der Sammlungsansicht reagieren, ist dies natürlich möglich. Es gibt immer noch den chaotischen Weg. Ich habe es mit einer "Größenzelle" gemacht, dh einer nicht angezeigten UICollectionViewCell, die der Controller nur zur Berechnung der Zellengrößen aufbewahrt.
Dieser Ansatz besteht aus zwei Teilen. Der erste Teil besteht darin, dass der Delegierte der Sammlungsansicht die richtige Zellengröße berechnet, indem er die Breite der Sammlungsansicht berücksichtigt und die Größe der Zelle mithilfe der Größenzelle berechnet.
In Ihrem UICollectionViewDelegateFlowLayout implementieren Sie eine Methode wie die folgende:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
// NOTE: here is where we say we want cells to use the width of the collection view
let requiredWidth = collectionView.bounds.size.width
// NOTE: here is where we ask our sizing cell to compute what height it needs
let targetSize = CGSize(width: requiredWidth, height: 0)
/// NOTE: populate the sizing cell's contents so it can compute accurately
self.sizingCell.label.text = items[indexPath.row]
let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
return adequateSize
}
Dadurch wird in der Sammlungsansicht die Breite der Zelle basierend auf der umschließenden Sammlungsansicht festgelegt. Anschließend wird die Größenzelle aufgefordert, die Höhe zu berechnen.
Der zweite Teil besteht darin, die Größenzelle dazu zu bringen, ihre eigenen AL-Einschränkungen zur Berechnung der Höhe zu verwenden. Dies kann schwieriger sein als es sein sollte, da mehrzeilige UILabels effektiv einen zweistufigen Layoutprozess erfordern. Die Arbeit wird in der Methode erledigt preferredLayoutSizeFittingSize
, die so ist:
/*
Computes the size the cell will need to be to fit within targetSize.
targetSize should be used to pass in a width.
the returned size will have the same width, and the height which is
calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
can fit within that width.
*/
func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {
// save original frame and preferredMaxLayoutWidth
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
// assert: targetSize.width has the required width of the cell
// step1: set the cell.frame to use that width
var frame = self.frame
frame.size = targetSize
self.frame = frame
// step2: layout the cell
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.label.bounds.size.width
// assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width
// step3: compute how tall the cell needs to be
// this causes the cell to compute the height it needs, which it does by asking the
// label what height it needs to wrap within its current bounds (which we just set).
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// assert: computedSize has the needed height for the cell
// Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
let newSize = CGSize(width:targetSize.width,height:computedSize.height)
// restore old frame and preferredMaxLayoutWidth
self.frame = originalFrame
self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
(Dieser Code wurde aus dem Apple-Beispielcode aus dem Beispielcode der WWDC2014-Sitzung in "Advanced Collection View" angepasst.)
Ein paar Punkte zu beachten. Mit layoutIfNeeded () wird das Layout der gesamten Zelle erzwungen, um die Breite des Etiketts zu berechnen und festzulegen. Das reicht aber nicht. Ich glaube, Sie müssen auch festlegen preferredMaxLayoutWidth
, dass das Etikett diese Breite mit dem automatischen Layout verwendet. Und nur dann können Sie verwenden systemLayoutSizeFittingSize
, um die Zelle dazu zu bringen, ihre Höhe unter Berücksichtigung des Etiketts zu berechnen.
Mag ich diesen Ansatz? Nein!! Es fühlt sich viel zu komplex an und das Layout wird zweimal ausgeführt. Solange die Leistung kein Problem darstellt, möchte ich das Layout zur Laufzeit lieber zweimal ausführen, als es zweimal im Code definieren zu müssen. Dies scheint die einzige andere Alternative zu sein.
Ich hoffe, dass Zellen mit Selbstgröße irgendwann anders funktionieren und dies alles viel einfacher wird.
Beispielprojekt, das es bei der Arbeit zeigt.
Aber warum nicht einfach selbstleuchtende Zellen verwenden?
Theoretisch sollten die neuen Funktionen von iOS8 für "Zellen mit Selbstgröße" dies unnötig machen. Wenn Sie eine Zelle mit Auto Layout (AL) definiert haben, sollte die Sammlungsansicht so intelligent sein, dass sie sich selbst dimensionieren und korrekt anordnen kann. In der Praxis habe ich keine Beispiele gesehen, die dazu geführt haben, dass dies mit mehrzeiligen Etiketten funktioniert. Ich denke, das liegt zum Teil daran, dass der selbstgrößende Zellmechanismus immer noch fehlerhaft ist.
Aber ich wette, es liegt hauptsächlich an der üblichen Schwierigkeit des automatischen Layouts und der Beschriftungen, dass UILabels einen grundsätzlich zweistufigen Layoutprozess erfordern. Mir ist nicht klar, wie Sie beide Schritte mit Zellen mit Selbstgröße ausführen können.
Und wie gesagt, das ist wirklich ein Job für ein anderes Layout. Es gehört zum Kern des Flow-Layouts, Dinge mit einer Größe zu positionieren, anstatt eine Breite festzulegen, und sie können ihre Höhe auswählen.
Und was ist mit PreferredLayoutAttributesFittingAttributes :?
Die preferredLayoutAttributesFittingAttributes:
Methode ist ein roter Hering, denke ich. Dies kann nur mit dem neuen selbstleimenden Zellmechanismus verwendet werden. Dies ist also nicht die Antwort, solange dieser Mechanismus unzuverlässig ist.
Und was ist los mit systemlayoutSizeFittingSize:?
Sie haben Recht, die Dokumente sind verwirrend.
Die Dokumente auf systemLayoutSizeFittingSize:
und systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
beide schlagen vor, dass Sie nur UILayoutFittingCompressedSize
und UILayoutFittingExpandedSize
als die übergeben sollten targetSize
. Die Methodensignatur selbst, die Header-Kommentare und das Verhalten der Funktionen zeigen jedoch an, dass sie auf den genauen Wert des targetSize
Parameters reagieren .
Wenn Sie die festlegen UICollectionViewFlowLayoutDelegate.estimatedItemSize
, um den neuen Mechanismus für Zellen mit Selbstgröße zu aktivieren, scheint dieser Wert tatsächlich als targetSize übergeben zu werden. Und UILabel.systemLayoutSizeFittingSize
scheint genau die gleichen Werte wie zurückzugeben UILabel.sizeThatFits
. Dies ist verdächtig, da das Argument to systemLayoutSizeFittingSize
ein grobes Ziel und das Argument to sizeThatFits:
eine maximale Umschreibungsgröße sein soll.
Mehr Ressourcen
Es ist zwar traurig zu glauben, dass eine solche Routineanforderung "Forschungsressourcen" erfordern sollte, aber ich denke, dass dies der Fall ist. Gute Beispiele und Diskussionen sind: