Estoy tratando de auto dimensionarme UICollectionViewCells
con Auto Layout, pero parece que no puedo hacer que las celdas se ajusten al contenido. Tengo problemas para comprender cómo se actualiza el tamaño de la celda a partir del contenido de lo que está dentro de contentView de la celda.
Aquí está la configuración que he probado:
- Personalizado
UICollectionViewCell
con unUITextView
en su contentView. - El desplazamiento para el
UITextView
está deshabilitado. - La restricción horizontal de contentView es: "H: | [_textView (320)]", es decir,
UITextView
está anclado a la izquierda de la celda con un ancho explícito de 320. - La restricción vertical de contentView es: "V: | -0 - [_ textView]", es decir, el
UITextView
anclado en la parte superior de la celda. - El
UITextView
tiene un conjunto altura limitación para una constante que laUITextView
informes se encaje el texto.
Así es como se ve con el fondo de la celda en rojo, y el UITextView
fondo establecido en Azul:
Puse el proyecto con el que he estado jugando en GitHub aquí .
Respuestas:
Actualizado para Swift 5
preferredLayoutAttributesFittingAttributes
renombradopreferredLayoutAttributesFitting
y usar dimensionamiento automáticoActualizado para Swift 4
systemLayoutSizeFittingSize
renombrado asystemLayoutSizeFitting
Actualizado para iOS 9
Después de ver que mi solución GitHub se rompió en iOS 9, finalmente tuve el tiempo para investigar el problema por completo. Ahora he actualizado el repositorio para incluir varios ejemplos de diferentes configuraciones para celdas de tamaño propio. Mi conclusión es que las células auto dimensionantes son geniales en teoría pero desordenadas en la práctica. Una palabra de precaución al proceder con las células auto dimensionantes.
TL; DR
Mira mi proyecto GitHub
Las celdas de tamaño automático solo son compatibles con el diseño de flujo, así que asegúrese de que eso es lo que está utilizando.
Hay dos cosas que debe configurar para que las celdas de tamaño automático funcionen.
1. Establecer
estimatedItemSize
enUICollectionViewFlowLayout
El diseño del flujo se volverá dinámico en la naturaleza una vez que configure la
estimatedItemSize
propiedad.2. Agregue soporte para dimensionar su subclase de celda
Esto viene en 2 sabores; Diseño automático o anulación personalizada de
preferredLayoutAttributesFittingAttributes
.Crear y configurar celdas con diseño automático
No voy a entrar en detalles sobre esto ya que hay un brillante publicación SO sobre la configuración de restricciones para una celda. Solo tenga cuidado de que Xcode 6 rompió un montón de cosas con iOS 7, por lo que, si es compatible con iOS 7, deberá hacer cosas como asegurarse de que autoresizingMask esté configurado en contentView de la celda y que los límites de contentView estén establecidos como los límites de la celda cuando la celda está cargada (es decir
awakeFromNib
).Lo que debe tener en cuenta es que su celda debe estar más seriamente restringida que una celda de vista de tabla. Por ejemplo, si desea que su ancho sea dinámico, su celda necesita una restricción de altura. Del mismo modo, si desea que la altura sea dinámica, necesitará una restricción de ancho para su celda.
Implementar
preferredLayoutAttributesFittingAttributes
en su celda personalizadaCuando se llama a esta función, su vista ya se ha configurado con contenido (es decir,
cellForItem
se ha llamado). Suponiendo que sus restricciones se hayan establecido adecuadamente, podría tener una implementación como esta:NOTA En iOS 9, el comportamiento cambió un poco y podría causar fallas en su implementación si no tiene cuidado (vea más aquí ). Cuando implementas
preferredLayoutAttributesFittingAttributes
, debe asegurarse de que solo cambie el marco de sus atributos de diseño una vez. Si no hace esto, el diseño llamará a su implementación indefinidamente y finalmente se bloqueará. Una solución es almacenar en caché el tamaño calculado en su celda e invalidarlo cada vez que reutilice la celda o cambie su contenido como lo he hecho con elisHeightCalculated
propiedad.Experimenta tu diseño
En este punto, debería tener celdas dinámicas "funcionales" en su collectionView. Todavía no he encontrado suficiente la solución lista para usar durante mis pruebas, así que siéntase libre de comentar. Todavía se siente como
UITableView
gana la batalla por el tamaño dinámico en mi humilde opinión.Advertencias
Tenga en cuenta que si está utilizando celdas prototipo para calcular el tamaño estimado del artículo, esto se romperá si su XIB usa clases de tamaño . La razón de esto es que cuando carga su celda desde un XIB, su clase de tamaño se configurará con
Undefined
. Esto solo se romperá en iOS 8 y superior, ya que en iOS 7 la clase de tamaño se cargará en función del dispositivo (iPad = Regular-Any, iPhone = Compact-Any). Puede establecer el tamaño estimado del artículo sin cargar el XIB, o puede cargar la celda desde el XIB, agregarlo a la colecciónView (esto establecerá la colección de caracteres), realizar el diseño y luego eliminarlo de la supervista. Alternativamente, también podría hacer que su celda anule eltraitCollection
getter y devuelva los rasgos apropiados. Tu decides.Avíseme si me perdí algo, espero haber ayudado y buena suerte codificando
fuente
estimatedItemSize
conduce a bloqueos: parece haber un gran error en la forma en que el diseño automático intenta procesar la UICollectionViewCell.En iOS10 hay una nueva constante llamada
UICollectionViewFlowLayout.automaticSize
(anteriormenteUICollectionViewFlowLayoutAutomaticSize
), así que en su lugar:puedes usar esto:
Tiene un mejor rendimiento, especialmente cuando las celdas en su vista de colección tienen wid constante
Acceso al diseño de flujo:
Swift 5 actualizado:
fuente
collectionViewLayout
y simplemente verificar que sea de tipoUICollectionViewFlowLayout
Algunos cambios clave en la respuesta de Daniel Galasko solucionaron todos mis problemas. Desafortunadamente, no tengo suficiente reputación para comentar directamente (todavía).
En el paso 1, cuando use el diseño automático, simplemente agregue una vista UIView principal a la celda. TODO dentro de la celda debe ser una subvista del padre. Eso respondió a todos mis problemas. Si bien Xcode agrega esto para UITableViewCells automáticamente, no lo hace (pero debería) para UICollectionViewCells. Según los documentos :
Luego omita el paso 3 por completo. No es necesario
fuente
contentView
, Xcode se queja de que entra en conflicto con la propiedad existente. Intenté agregar subvistasself.contentView
y establecer restricciones a eso, luego la aplicación se bloquea.En iOS 10+, este es un proceso muy simple de 2 pasos.
Asegúrese de que todos los contenidos de sus celdas se coloquen dentro de una sola UIView (o dentro de un descendiente de UIView como UIStackView que simplifica mucho la auto-distribución). Al igual que con el cambio dinámico de UITableViewCells, toda la jerarquía de vistas debe tener restricciones configuradas, desde el contenedor más externo hasta la vista más interna. Eso incluye restricciones entre UICollectionViewCell y la vista secundaria inmediata
Indique al flowlayout de su UICollectionView que dimensione automáticamente
fuente
UICollectionViewFlowLayout.automaticSize
ha cambiado el nombreUICollectionViewFlowLayoutAutomaticSize
.Agregue flowLayout en viewDidLoad ()
Además, configure una UIView como mainContainer para su celda y agregue todas las vistas requeridas dentro de ella.
Consulte este increíble tutorial alucinante para obtener más información: UICollectionView con celda de tamaño automático utilizando autolayout en iOS 9 y 10
fuente
EDITAR 19/11/19: para iOS 13, solo use UICollectionViewCompositionalLayout con alturas estimadas. No pierdas tu tiempo lidiando con esta API rota.
Después de luchar con esto durante algún tiempo, noté que el cambio de tamaño no funciona para UITextViews si no deshabilita el desplazamiento:
fuente
contentView misterio de anclaje:
En un caso extraño esto
no funcionaría. Se agregaron cuatro anclas explícitas a contentView y funcionó.
y como siempre
en
YourLayout: UICollectionViewFlowLayout
¿Quién sabe? Podría ayudar a alguien.
Crédito
https://www.vadimbulavin.com/collection-view-cells-self-sizing/
Tropecé con la punta allí, nunca lo vi en ningún otro lugar en todos los artículos de los 1000 sobre esto.
fuente
Hice una altura de celda dinámica de la vista de colección. Aquí está el repositorio de git hub .
Y, descubra por qué se llama más preferido una vez a PreferredLayoutAttributesFittingAttributes. En realidad, se llamará al menos 3 veces.
La imagen de registro de la consola:
1er preferidoLayoutAttributesFittingAttributes :
El layoutAttributes.frame.size.height es el estado actual 57.5 .
2do preferidoLayoutAttributesFittingAttributes :
La altura del marco de la celda cambió a 534.5 como se esperaba. Pero, la vista de colección sigue siendo de altura cero.
3er preferidoLayoutAttributesFittingAttributes :
Puede ver que la altura de la vista de colección cambió de 0 a 477 .
El comportamiento es similar a manejar desplazamiento:
Al principio, pensé que este método solo llamaba una vez. Así que codifiqué lo siguiente:
Esta línea:
hará que el sistema llame al bucle infinito y se bloquee la aplicación.
Cualquier cambio de tamaño, validará todas las celdas 'PreferredAttributesFittingAttributes' de todas las celdas una y otra vez hasta que las posiciones de cada celda (es decir, marcos) ya no cambien.
fuente
Además de las respuestas anteriores,
Solo asegúrese de establecer la propiedad estimadaItemSize de UICollectionViewFlowLayout en algún tamaño y no implemente sizeForItem: atIndexPath método de delegado .
Eso es.
fuente
Si implementa el método UICollectionViewDelegateFlowLayout:
- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath
Cuando llame
collectionview performBatchUpdates:completion:
, la altura del tamaño se usará ensizeForItemAtIndexPath
lugar depreferredLayoutAttributesFittingAttributes
.El proceso de renderizado
performBatchUpdates:completion
pasará por el métodopreferredLayoutAttributesFittingAttributes
pero ignora los cambios.fuente
A quien pueda ayudar,
Tuve ese accidente desagradable si
estimatedItemSize
estaba configurado. Incluso si devuelvo 0 ennumberOfItemsInSection
. Por lo tanto, las celdas en sí y su diseño automático no fueron la causa del bloqueo ... La colecciónView simplemente se bloqueó, incluso cuando estaba vacía, solo porqueestimatedItemSize
estaba configurado para el autoajuste.En mi caso, reorganicé mi proyecto, desde un controlador que contiene un collectionView a un collectionViewController, y funcionó.
Imagínate.
fuente
collectionView.collectionViewLayout.invalidateLayout()
despuéscollectionView.reloadData()
.Para cualquiera que haya intentado todo sin suerte, esto es lo único que lo hizo funcionar para mí. Para las etiquetas multilínea dentro de la celda, intente agregar esta línea mágica:
Más información: aquí
¡Salud!
fuente
El método de ejemplo anterior no se compila. Aquí hay una versión corregida (pero no comprobada si funciona o no).
fuente
Actualiza más información:
Si usa
flowLayout.estimatedItemSize
, sugiera usar iOS8.3 versión posterior. Antes de iOS8.3, se bloqueará[super layoutAttributesForElementsInRect:rect];
. El mensaje de error es*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
En segundo lugar, en la versión iOS8.x, la
flowLayout.estimatedItemSize
configuración de inserción de sección diferente no funcionó. es decir, la función:(UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:
.fuente
La solución comprende 4 pasos importantes:
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
contentView
a los bordes de la celda porquecontentView
evita el auto dimensionamiento, dado que no tiene habilitado el diseño automático.collectionView(:cellForItemAt:)
para limitar el ancho de contentView al ancho de collectionView.Sucede de la siguiente manera; configuraremos la
maxWidth
variable de la celda al ancho de CollectionView encollectionView(:cellForItemAt:)
y enmaxWidth
eldidSet
método, estableceremos widthAnchor.constant en maxWidth.Como desea habilitar el autoajuste de UITextView, tiene un paso adicional para;
4. Calcule y establezca la alturaAnchor.constant de UITextView.
Entonces, cada vez que se establece el ancho de contentView ajustaremos la altura de UITextView a lo largo
didSet
demaxWidth
.Inside UICollectionViewCell:
Estos pasos te darán el resultado deseado. Aquí hay una esencia completa ejecutable
Gracias a Vadim Bulavin por su publicación de blog clara y esclarecedora - Autoedición de las celdas de la vista de colección: tutorial paso a paso
fuente
Intenté usar
estimatedItemSize
pero había un montón de errores al insertar y eliminar celdas siestimatedItemSize
no era exactamente igual a la altura de la celda. Dejé de configurarestimatedItemSize
e implementé celdas dinámicas utilizando un prototipo de celda. así es como se hace:crea este protocolo:
implementar este protocolo en su costumbre
UICollectionViewCell
:ahora haga que su controlador se ajuste y
UICollectionViewDelegateFlowLayout
, en él, tenga este campo:se usará como una celda prototipo para enlazar datos y luego determinar cómo esos datos afectaron la dimensión en la que desea ser dinámico
finalmente, el
UICollectionViewDelegateFlowLayout's
collectionView(:layout:sizeForItemAt:)
tiene que ser implementado:y eso es. Devolver el tamaño de la celda de
collectionView(:layout:sizeForItemAt:)
esta manera evitando que tenga que usarestimatedItemSize
, e insertar y eliminar celdas funciona perfectamente.fuente