collection uicollectionview ios9 uicollectionviewcell uicollectionviewlayout xcode7-beta5

uicollectionview resize cell



el comportamiento de UICollectionViewFlowLayout no está definido, porque el ancho de la celda es mayor que el ancho de collectionView (10)

2015-08-18 16: 07: 51.523 Ejemplo [16070: 269647] el comportamiento de UICollectionViewFlowLayout no está definido porque: 2015-08-18 16: 07: 51.523
Ejemplo [16070: 269647] el ancho del elemento debe ser menor que el ancho de UICollectionView menos los valores izquierdo y derecho de la sección, menos los valores izquierdo y derecho del interior del contenido.
2015-08-18 16: 07: 51.524 Ejemplo [16070: 269647] La ​​instancia relevante de UICollectionViewFlowLayout es, y se adjunta a; animaciones = {posición =; límites.origen =; límites.size =; }; capa =; contentOffset: {0, 0}; contentSize: {1024, 770}> diseño de vista de colección:. 2015-08-18 16: 07: 51.524 Ejemplo [16070: 269647] Cree un punto de interrupción simbólico en UICollectionViewFlowLayoutBreakForInvalidSizes para capturar esto en el depurador.

Esto es lo que obtengo, lo que hago es

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { return CGSizeMake(self.collectionView!.frame.size.width - 20, 66) }

cuando giro de horizontal a vertical, la consola muestra este mensaje de error solo en iOS 9, ¿alguien sabe qué sucede y si hay una solución para esto?


Después de experimentar un poco, esto también parece estar relacionado con la forma en que usted diseña su collectionView.

El tl; dr es: Use AutoLayout, no autoresizingMask.

Entonces, para el núcleo del problema, las mejores soluciones que he encontrado para manejar el cambio de orientación usan el siguiente código, lo cual tiene sentido:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: { (context) in self.collectionView.collectionViewLayout.invalidateLayout() }, completion: nil) }

Sin embargo , todavía hay situaciones en las que puede obtener la advertencia de tamaño del elemento. Para mí es si estoy en horizontal en una pestaña, cambiar a otra pestaña, rotar a vertical y luego regresar a la pestaña anterior. Intenté invalidar el diseño en willAppear, willLayout, todos los sospechosos habituales, pero no hubo suerte. De hecho, incluso si llama a invalidateLayout antes de super.willAppear() todavía recibirá la advertencia.

Y, en última instancia, este problema está vinculado al tamaño de los límites de collectionView que se actualizan antes de que se solicite al delegado el itemSize.

Entonces, con eso en mente, traté de usar AutoLayout en lugar de collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] , y con eso, ¡el problema se resolvió! (Uso SnapKit para hacer AutoLayout para no arrancarme el pelo constantemente). También solo tiene que invalidateLayout en viewWillTransition (sin coordinator.animate , así como lo ha hecho en su ejemplo), y también invalidateLayout en la parte inferior de viewWillAppear(:) . Eso parece cubrir todas las situaciones.

No sé si está utilizando el autoresizing o no, sería interesante saber si mi teoría / solución funciona para todos.


Esto es lo que necesitas:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { //Get frame width let width = self.view.frame.width //I want a width of 418 and height of 274 (Aspect ratio 209:137) with a margin of 24 on each side of the cell (See insetForSectionAt 24 + 24 = 48). So, check if a have that much screen real estate. if width > (418 + 48) { //If I do return the size I want return CGSize(width: 418, height: 274) }else{ //Get new width. Frame width minus the margins I want to maintain let newWidth = (width - 48) //If not calculate the new height that maintains the aspect ratio I want. NewHeight = (originalHeight / originalWidth) * newWidth let height = (274 / 418) * newWidth //Return the new size that is Aspect ratio 209:137 return CGSize(width: newWidth, height: height) } } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 33 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return 33 }


Esto funciona para mí:

Invalidar el diseño en rotación:

override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() collectionView.collectionViewLayout.invalidateLayout() }

Tiene una función separada para calcular el ancho disponible:

Nota: teniendo en cuenta tanto la inserción de la sección como la del contenido.

// MARK: - Helpers func calculateCellWidth(for collectionView: UICollectionView, section: Int) -> CGFloat { var width = collectionView.frame.width let sectionInset = self.collectionView(collectionView, layout: collectionView.collectionViewLayout, insetForSectionAt: section) let contentInset = collectionView.contentInset width = width - sectionInset.left - sectionInset.right width = width - contentInset.left - contentInset.right return width }

Y luego, por supuesto, devolviendo finalmente el tamaño del artículo:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: calculateCellWidth(for: collectionView, section: indexPath.section), height: 60) }

Creo que es importante tener en cuenta que esto funciona porque la invalidación de un diseño no activará un nuevo cálculo del tamaño de la celda inmediatamente, sino durante el próximo ciclo de actualización del diseño. Esto significa que una vez que se llama finalmente a la devolución de llamada del tamaño del elemento, la vista de colección tiene el marco correcto, lo que permite un tamaño exacto.

Voila!


Esto sucede cuando la vista de la colección cambia de tamaño a algo menos ancho (por ejemplo, vaya del modo horizontal al vertical) y la celda se vuelve demasiado grande para ajustarse.

¿Por qué la celda se está volviendo demasiado grande, ya que el diseño de flujo de vista de colección debe llamarse y devolver un tamaño adecuado?

collectionView (collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath)

Actualización para incluir Swift 4

@objc override func collectionView (_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}

Esto se debe a que esta función no se llama, o al menos no de inmediato.

Lo que sucede es que la subclase de diseño de flujo de la vista de colección no anula la función shouldInvalidateLayoutForBoundsChange , que returns false de forma predeterminada.

Cuando este método devuelve falso, la vista de colección primero intenta ir con el tamaño de celda actual, detecta un problema (que registra la advertencia) y luego llama al diseño del flujo para cambiar el tamaño de la celda.

Esto significa 2 cosas:

1 - La advertencia en sí misma no es perjudicial.

2: puede deshacerse de él simplemente anulando la función shouldInvalidateLayoutForBoundsChange para devolver verdadero. En ese caso, el diseño del flujo siempre se llamará cuando cambien los límites de la vista de colección.


Estoy usando una subclase de UICollectionViewFlowLayout y usando su propiedad itemSize para especificar el tamaño de celda (en lugar del collectionView:sizeForItemAtIndexPath: método de delegado). Cada vez que giro la pantalla a un ancho menor (p. Ej., Paisaje -> retrato) recibo esta enorme advertencia en cuestión.

Pude arreglarlo haciendo 2 pasos.

Paso 1: En la UICollectionViewFlowLayout prepareLayout() la subclase prepareLayout() , mueva super.prepareLayout() a después de donde se establece self.itemSize . Creo que esto hace que la súper clase use el valor correcto de itemSize .

import UIKit extension UICollectionViewFlowLayout { var collectionViewWidthWithoutInsets: CGFloat { get { guard let collectionView = self.collectionView else { return 0 } let collectionViewSize = collectionView.bounds.size let widthWithoutInsets = collectionViewSize.width - self.sectionInset.left - self.sectionInset.right - collectionView.contentInset.left - collectionView.contentInset.right return widthWithoutInsets } } } class StickyHeaderCollectionViewLayout: UICollectionViewFlowLayout { // MARK: - Variables let cellAspectRatio: CGFloat = 3/1 // MARK: - Layout override func prepareLayout() { self.scrollDirection = .Vertical self.minimumInteritemSpacing = 1 self.minimumLineSpacing = 1 self.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: self.minimumLineSpacing, right: 0) let collectionViewWidth = self.collectionView?.bounds.size.width ?? 0 self.headerReferenceSize = CGSize(width: collectionViewWidth, height: 40) // cell size let itemWidth = collectionViewWidthWithoutInsets self.itemSize = CGSize(width: itemWidth, height: itemWidth/cellAspectRatio) // Note: call super last if we set itemSize super.prepareLayout() } // ... }

Tenga en cuenta que el cambio anterior hará que el tamaño del diseño cambie cuando la pantalla gira deja de funcionar. Aquí es donde entra el paso 2.

Paso 2: coloque esto en el controlador de vista que contiene la vista de colección.

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator) collectionView.collectionViewLayout.invalidateLayout() }

Ahora la advertencia se ha ido :)

Algunas notas:

  1. Asegúrese de que está agregando restricciones a collectionView y no está usando collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

  2. Asegúrese de que no está llamando invalidateLayout en viewWillTransitionToSize() porque el ancho de una celda de borde a borde en horizontal es mayor que el ancho del marco de la vista de colección en vertical. Ver abajo las referencias.

Referencias


He resuelto este problema utilizando safeAreaLayoutGuide .

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: (view.safeAreaLayoutGuide.layoutFrame.width), height: 80); }

y también tiene que anular esta función para admitir correctamente el modo retrato y paisaje.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { collectionView?.collectionViewLayout.invalidateLayout(); }


Ocurre porque el ancho de la celda de la vista de colección es mayor que el ancho de la vista de la colección después de la rotación. Supongamos que tiene una pantalla de 1024x768 y la vista de la colección llena la pantalla. Cuando el dispositivo está en posición horizontal, el ancho de la celda será self.collectionView.frame.size .width - 20 = 1004 y es mayor que el ancho de su vista de colección en retrato = 768. El depurador dice que "el ancho del elemento debe ser menor que el ancho de UICollectionView menos los valores de izquierda y derecha de las inserciones de sección, menos los espacios de contenido de la izquierda y los valores correctos ".


Puede verificar en el depurador si collectionView.contentOffset cambia a negativo en mi caso, cambia de (0,0) a (0,-40) . Puedes resolver este problema usando este método

if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) { self.automaticallyAdjustsScrollViewInsets = NO; }


Tuve el mismo problema. Arreglé esto al borrar la vista de mi colección de sus restricciones y restablecerlas en el Guión gráfico.


Tuve un problema similar

En mi caso, tuve una vista de colección y cuando pulsó en una de las celdas, se abrió una UITextField con un UITextField , para editar el elemento. Después de que la self.collectionView.contentInset.bottom desapareciera, self.collectionView.contentInset.bottom se configuró en 55 (originalmente 0).

Para solucionar mi problema, después de que la vista emergente desaparezca, estoy configurando manualmente contentInset en UIEdgeInsetsZero .

El problema original parece estar relacionado con la barra de predicción contextual que aparece en la parte superior del teclado. Cuando el teclado está oculto, la barra desaparece, pero el valor contentInset.bottom no se restaura al valor original.

Dado que su problema parece estar relacionado con el ancho y no con la altura de la celda, verifique si alguno de los valores de contentInset o layout.sectionInset son los mismos que usted estableció.