Error de iOS 10: UICollectionView recibió atributos de diseño para una celda con una ruta de índice que no existe
ios10 uicollectionviewlayout (18)
Cuando tenga un diseño personalizado, recuerde borrar el caché (UICollectionViewLayoutAttributes) mientras anula la función de preparación
override func prepare() {
super.prepare()
cache.removeAll()
}
Al ejecutar mi aplicación en un dispositivo con iOS 10, aparece este error:
UICollectionView recibió atributos de diseño para una celda con una ruta de índice que no existe
En iOS 8 y 9 funciona bien. He estado investigando y he descubierto que es algo relacionado con invalidar el diseño de la vista de colección. Traté de implementar esa solución sin éxito, por lo que me gustaría pedir ayuda directa. Esta es mi vista de jerarquía:
->Table view
->Each cell of table is a custom collection view [GitHub Repo][1]
->Each item of collection view has another collection view
Lo que he intentado es insertar
[self.collectionView.collectionViewLayout invalidateLayout];
En el
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
de ambas vistas de colección.
También he tratado de invalidar el diseño antes de hacer una recarga de datos, no funciona ...
¿Alguien podría darme algunas instrucciones para tomar?
Después de pasar dos días de tiempo, el siguiente código resolvió el problema. Antes de cargar una vista de colección, escriba el siguiente código.
flowLayout.itemSize = CGSize(width: 92, height: 100)
Luego, el bloqueo se resolverá, pero encontrará un problema en las celdas de la vista de colección, ya que las celdas están comprimidas o no según su diseño. Luego solo una línea de código después de la línea de dirección de desplazamiento
override func prepareForReuse() {
super.prepareForReuse()
imagesCollectionHeight.constant = 100 // changing collectionview height constant
imageContainerWidth.constant = 100 //changing width constant of some view
self.layoutIfNeeded() // this line did the trick for me
}
Con la línea de código anterior, puede ajustar su diseño de celda de vista de colección.
Espero que ayude a alguien.
En mi caso, estaba cambiando la constante NSlayoutConstraint
self.collectionViewContacts.collectionViewLayout.invalidateLayout()
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
self.collectionViewContacts.reloadData()
resuelto el problema
En mi caso, estaba cambiando la constante de NSLayoutConstraint, ninguna de las soluciones anteriores funcionó para mí. gracias a @jeff ayan cuya respuesta me dio la pista. Resolví el problema con este código
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
Espero que ayude a alguien
Esto me sucedió cuando el número de celdas en collectionView cambió. Resulta que me faltaba invalidateLayout después de llamar a reloadData. Después de agregarlo, no he experimentado más bloqueos. Apple ha realizado algunas modificaciones en collectionViews en iOS10. Supongo que esa es la razón por la que no estamos experimentando el mismo problema en versiones anteriores.
Aquí está mi código final:
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
Esto también me sucedió a mí, pero fue porque mi
UICollectionViewDataSource
cambió, y no llamé a
-[UICollectionView reloadData]
.
En mi caso, tenía la siguiente estructura de datos:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.fooCollectionView) {
self.selectedFoo = self.foos[indexPath.row];
[self.barCollectionView reloadData];
self.fooCollectionView.hidden = YES;
self.barCollectionView.hidden = NO;
} else if (collectionView == self.barCollectionView) {
// do some stuff with the selected bar
self.selectedFoo = nil;
// This -reloadData call fixed my error. I thought I didn''t
// need it since my UICollectionView was hidden
[self.barCollectionView reloadData];
self.barCollectionView.hidden = YES;
self.fooCollectionView.hidden = NO;
}
}
Tenía dos
UICollectionView
s: uno para
Foo
s y otro para
Bar
s.
En mi
-collectionView:didSelectItemAtIndexPath:
tuve lo siguiente:
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
collecView.collectionViewLayout = flowLayout
collecView.delegate = self
collecView.dataSource = self
collecView.reloadData()
Sin la llamada
-reloadData
, vería el bloqueo cuando gire el dispositivo.
La respuesta anterior ayuda, pero si usa celdas de tamaño automático, su tamaño será incorrecto.
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.estimatedItemSize = CGSizeMake(60, 25);
layout.itemSize = UICollectionViewFlowLayoutAutomaticSize;
self.collectionView.collectionViewLayout = layout;
Resuelvo estos problemas reemplazando
[self.collectionView reloadData];
a
[self.collectionView reloadSections:indexSet];
La respuesta de @ Katrin ayudó mucho, pero pude lograr mejores resultados agregando una línea más:
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutSubviews() // <-- here it is :)
Ahora no puedo decir si podría reproducir el bloqueo con esta línea o no, pero supongo que hubo una ... Entonces, todavía no es una bala de plata, sino algo.
Llamar
invalidateLayout
no evitó el bloqueo en mi caso.
(Funcionó si el número de elementos en la vista de colección aumentaba pero no si disminuía).
Contexto: tengo un UICollectionView dentro de un UITableViewCell y cuando la celda de la tabla se reutiliza restablezco los delegados de la colección.
Solucioné el problema no invalidando la memoria caché, sino RECREANDO el objeto de diseño cada vez que restablezco el delegado, luego llamé a reloadData ():
foo.dataSource = newDataSource
foo.delegate = newDelegate
foo.fixLayoutBug()
foo.reloadData()
func fixLayoutBug() {
let oldLayout = collectionViewLayout as! UICollectionViewFlowLayout
let newLayout = UICollectionViewFlowLayout()
newLayout.estimatedItemSize = oldLayout.estimatedItemSize
newLayout.footerReferenceSize = oldLayout.footerReferenceSize
newLayout.headerReferenceSize = oldLayout.headerReferenceSize
newLayout.itemSize = oldLayout.itemSize
newLayout.minimumInteritemSpacing = oldLayout.minimumInteritemSpacing
newLayout.minimumLineSpacing = oldLayout.minimumLineSpacing
newLayout.scrollDirection = oldLayout.scrollDirection
newLayout.sectionFootersPinToVisibleBounds = oldLayout.sectionFootersPinToVisibleBounds
newLayout.sectionHeadersPinToVisibleBounds = oldLayout.sectionHeadersPinToVisibleBounds
newLayout.sectionInset = oldLayout.sectionInset
newLayout.sectionInsetReference = oldLayout.sectionInsetReference
collectionViewLayout = newLayout
}
Lo siguiente lo hizo para mí:
[self.collectionView reloadData]; [self.collectionView layoutIfNeeded];
Este código parece obligar a collectionView a desplazarse a la primera celda antes de volver a cargar los datos, lo que parece causar el error en mi caso.
Me enfrenté al problema similar mientras mostraba-ocultaba la vista de colección usando el cambio animado de restricciones. Mi solución se basa en las respuestas existentes.
struct Bar { let name: String }
struct Foo { let name: String; let bars: [Bar] }
No es lo mejor reloadData cada vez (debe usar insertItems y deleteItems, e incluso reloadSections). Pero ... después de decir que en algunos casos es válido, puedes hacer esto:
collectionView.dataSource = nil
collectionView.delegate = nil
/*... All changes here. ... */
collectionView.dataSource = self
collectionView.delegate = self
collectionView.reloadData()
Restablecer el diseño de
UICollectionView
me resolvió el problema:
let layout = UICollectionViewFlowLayout()
collectionView?.collectionViewLayout = layout
Si desea mantener su posición de desplazamiento y corregir el bloqueo, puede usar esto:
collectionView.reloadData()
let context = collectionView.collectionViewLayout.invalidationContext(forBoundsChange: collectionView.bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)
También enfrenté este problema con 2 collectionViews en la misma vista porque agregué el mismo UICollectionViewFlowLayout en ambas colecciones:
let layout = UICollectionViewFlowLayout()
collectionView1.collectionViewLayout = layout
collectionView2.collectionViewLayout = layout
Por supuesto, se bloquea al recargar los datos si collectionView1 Data cambia. Si esto pudiera ayudar a alguien.
Tuve el problema al usar
RxDataSources
con
RxCollectionViewSectionedAnimatedDataSource
y lo resolví combinando dos respuestas.
Tengo que
invalidateLayout()
cuando recargo reactivamente mi colección:
...
.asDriver(onErrorJustReturn: [])
.do(onNext: { [weak self] _ in
DispatchQueue.main.async {
self?.collectionViewLayout.invalidateLayout()
self?.collectionView.layoutSubviews()
}
}).drive(collectionView.rx.items(dataSource: collectionController.dataSource))
.disposed(by: disposeBag)
y borrar la (s) matriz (s) de
cache
en el método de diseño
prepare()
.
Aquí está el código:
override func prepare() {
super.prepare()
cache.removeAll()
guard let collectionView = collectionView,
collectionView.numberOfSections > 0,
let delegate = delegate else {
return
}
...
Yo tuve este problema también.
En mi caso, se aplicó un
UICollectionViewLayout
personalizado a la vista de recopilación y el problema era que tenía datos obsoletos para la cantidad de celdas que deberían mostrarse.
Eso es definitivamente algo que puede verificar cuando vea los
UICollectionView received layout attributes for a cell with an index path that does not exist
reloadData()
resolver esto recreando la
collectionViewLayout
antes de llamar a
reloadData()
El problema probablemente se relacionó con que tengo una instancia separada para
dataSource
(es decir, no el controlador de vista que contiene el collectionView), y al intercambiar la fuente de datos, podría aparecer el bloqueo.