Parece que lo que está pidiendo es una forma de usar UICollectionView para producir un diseño como UITableView. Si eso es realmente lo que desea, la forma correcta de hacerlo es con una subclase UICollectionViewLayout personalizada (tal vez algo como SBTableLayout ).
Por otro lado, si realmente está preguntando si hay una forma limpia de hacer esto con el UICollectionViewFlowLayout predeterminado, entonces creo que no hay forma. Incluso con las celdas de tamaño propio de iOS8, no es sencillo. El problema fundamental, como usted dice, es que la maquinaria del diseño de flujo no proporciona forma de arreglar una dimensión y dejar que otra responda. (Además, incluso si pudiera, habría una complejidad adicional en torno a la necesidad de dos pasadas de diseño para dimensionar las etiquetas de varias líneas. Es posible que esto no se ajuste a la forma en que las celdas de tamaño propio desean calcular todo el tamaño a través de una llamada a systemLayoutSizeFittingSize).
Sin embargo, si aún desea crear un diseño similar a una vista de tabla con un diseño de flujo, con celdas que determinan su propio tamaño y responden naturalmente al ancho de la vista de la colección, por supuesto que es posible. Todavía queda el camino desordenado. Lo he hecho con una "celda de tamaño", es decir, una UICollectionViewCell no mostrada que el controlador guarda solo para calcular el tamaño de las celdas.
Este enfoque consta de dos partes. La primera parte es que el delegado de la vista de colección calcule el tamaño de celda correcto, tomando el ancho de la vista de colección y usando la celda de tamaño para calcular la altura de la celda.
En su UICollectionViewDelegateFlowLayout, implementa un método como este:
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
}
Esto hará que la vista de la colección establezca el ancho de la celda en función de la vista de la colección adjunta, pero luego le pedirá a la celda de tamaño que calcule la altura.
La segunda parte es hacer que la celda de tamaño use sus propias restricciones AL para calcular la altura. Esto puede ser más difícil de lo que debería ser, debido a la forma en que los UILabel de múltiples líneas requieren efectivamente un proceso de diseño de dos etapas. El trabajo se realiza en el método preferredLayoutSizeFittingSize
, que es así:
/*
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
}
(Este código está adaptado del código de muestra de Apple del código de muestra de la sesión WWDC2014 en "Vista de colección avanzada").
Un par de puntos para notar. Está usando layoutIfNeeded () para forzar el diseño de toda la celda, con el fin de calcular y establecer el ancho de la etiqueta. Pero eso no es suficiente. Creo que también debe configurar preferredMaxLayoutWidth
para que la etiqueta use ese ancho con Diseño automático. Y solo entonces se puede usar systemLayoutSizeFittingSize
para que la celda calcule su altura teniendo en cuenta la etiqueta.
¿Me gusta este enfoque? ¡¡No!! Se siente demasiado complejo y hace el diseño dos veces. Pero siempre que el rendimiento no se convierta en un problema, prefiero realizar el diseño dos veces en tiempo de ejecución que tener que definirlo dos veces en el código, que parece ser la única otra alternativa.
Mi esperanza es que eventualmente las células de tamaño automático funcionen de manera diferente y todo esto se volverá mucho más simple.
Proyecto de ejemplo que lo muestra en funcionamiento.
Pero, ¿por qué no usar simplemente células de tamaño propio?
En teoría, las nuevas instalaciones de iOS8 para "celdas de tamaño propio" deberían hacer esto innecesario. Si ha definido una celda con Auto Layout (AL), entonces la vista de colección debería ser lo suficientemente inteligente como para permitir que se dimensione y se distribuya correctamente. En la práctica, no he visto ningún ejemplo que haya logrado que esto funcione con etiquetas de varias líneas. Creo que esto se debe en parte a que el mecanismo de la celda de tamaño automático todavía tiene errores.
Pero apuesto a que se debe principalmente a la complicación habitual del diseño automático y las etiquetas, que es que UILabels requiere un proceso de diseño básicamente de dos pasos. No tengo claro cómo se pueden realizar ambos pasos con células de tamaño propio.
Y como dije, este es realmente un trabajo para un diseño diferente. Es parte de la esencia del diseño de flujo que coloca las cosas que tienen un tamaño, en lugar de fijar un ancho y les permite elegir su altura.
¿Y qué hay de favoriteLayoutAttributesFittingAttributes:?
El preferredLayoutAttributesFittingAttributes:
método es una pista falsa, creo. Eso solo está ahí para ser utilizado con el nuevo mecanismo de celda de tamaño propio. Entonces, esta no es la respuesta mientras ese mecanismo no sea confiable.
¿Y qué pasa con systemlayoutSizeFittingSize :?
Tienes razón, los documentos son confusos.
Los documentos en systemLayoutSizeFittingSize:
y systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
ambos sugieren que solo debe pasar UILayoutFittingCompressedSize
y UILayoutFittingExpandedSize
como targetSize
. Sin embargo, la propia firma del método, los comentarios del encabezado y el comportamiento de las funciones indican que están respondiendo al valor exacto del targetSize
parámetro.
De hecho, si configura el UICollectionViewFlowLayoutDelegate.estimatedItemSize
, para habilitar el nuevo mecanismo de celda de auto-tamaño, ese valor parece pasar como targetSize. Y UILabel.systemLayoutSizeFittingSize
parece devolver exactamente los mismos valores que UILabel.sizeThatFits
. Esto es sospechoso, dado que systemLayoutSizeFittingSize
se supone que el argumento to es un objetivo aproximado y sizeThatFits:
se supone que el argumento to tiene un tamaño de circunscripción máximo.
Más recursos
Si bien es triste pensar que un requisito tan rutinario debería requerir "recursos de investigación", creo que sí. Buenos ejemplos y discusiones son:
Hay una forma más limpia de hacer esto que algunas de las otras respuestas aquí, y funciona bien. Debe ser eficaz (las vistas de la colección se cargan rápido, sin pases de diseño automático innecesarios, etc.) y no tiene ningún "número mágico" como un ancho de vista de colección fijo. Cambiar el tamaño de la vista de la colección, por ejemplo, en rotación, y luego invalidar el diseño también debería funcionar muy bien.
1. Cree la siguiente subclase de diseño de flujo
2. Registre su vista de colección para el tamaño automático
3. Use el ancho predefinido + alto personalizado en su subclase de celda
fuente
Una forma sencilla de hacerlo en iOS 9 en unas pocas líneas de códigos: el ejemplo de la forma horizontal (fijando su altura a la altura de la Vista de colección):
Inicie su diseño de flujo de vista de colección con un
estimatedItemSize
para habilitar la celda de tamaño propio:Implemente el Delegado de diseño de vista de colección (en su Controlador de vista la mayor parte del tiempo)
collectionView:layout:sizeForItemAtIndexPath:
,. El objetivo aquí es establecer la altura (o ancho) fija en la dimensión Vista de colección. El valor 10 puede ser cualquier cosa, pero debe establecerlo en un valor que no rompa las restricciones:Anule su
preferredLayoutAttributesFittingAttributes:
método de celda personalizado , esta parte realmente calcula el ancho de su celda dinámica en función de sus restricciones de diseño automático y la altura que acaba de establecer:fuente
UILabel
, cuyo salto de línea afecta la altura intrínseca de la celda? Este es un caso común que solía requerir todos los backflips que describo. Me pregunto si iOS lo ha solucionado desde mi respuesta.Intente arreglar su ancho en los atributos de diseño preferidos:
Naturalmente, también desea asegurarse de configurar su diseño correctamente para:
Aquí hay algo que puse en Github que usa celdas de ancho constante y admite tipo dinámico para que la altura de las celdas se actualice a medida que cambia el tamaño de fuente del sistema.
fuente
SÍ, se puede hacer usando el diseño automático mediante programación y estableciendo restricciones en el guión gráfico o xib. Debe agregar una restricción para que el tamaño del ancho permanezca constante y establecer una altura mayor o igual a.
http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatic/
http://www.cocoanetics.com/2013/08/variable-sized-items-in-uicollectionview/
Espero que esto sea útil y resuelve tu problema.
fuente
cellForItemAtIndexPath
actualizar la restricción:cell.constraintItemWidth.constant = UIScreen.main.bounds.width
. Mi aplicación es solo portátil. Probablemente desee hacerloreloadData
después de un cambio de orientación para actualizar la restricción.