Tengo UICollectionView
un FLowLayout. Funcionará como espero la mayor parte del tiempo, pero de vez en cuando una de las celdas no se ajusta correctamente. Por ejemplo, la celda que debería estar en la primera "columna" de la tercera fila si en realidad está al final de la segunda fila y solo hay un espacio vacío donde debería estar (vea el diagrama a continuación). Todo lo que puede ver de esta celda roja es el lado izquierdo (el resto está cortado) y el lugar donde debería estar está vacío.
Esto no sucede de manera constante; no siempre es la misma fila. Una vez que ha sucedido, puedo desplazarme hacia arriba y luego hacia atrás y la celda se habrá arreglado sola. O, cuando presiono la celda (que me lleva a la siguiente vista presionando) y luego retrocedo, veré la celda en la posición incorrecta y luego saltará a la posición correcta.
La velocidad de desplazamiento parece facilitar la reproducción del problema. Cuando me desplazo lentamente, todavía puedo ver la celda en la posición incorrecta de vez en cuando, pero luego saltará a la posición correcta de inmediato.
El problema comenzó cuando agregué las inserciones de las secciones. Anteriormente, tenía las celdas casi alineadas con los límites de la colección (poco o ningún inserto) y no noté el problema. Pero esto significaba que las partes derecha e izquierda de la vista de colección estaban vacías. Es decir, no se pudo desplazar. Además, la barra de desplazamiento no estaba alineada a la derecha.
Puedo hacer que el problema ocurra tanto en Simulator como en un iPad 3.
Supongo que el problema se debe a las inserciones de las secciones izquierda y derecha ... Pero si el valor es incorrecto, entonces esperaría que el comportamiento fuera consistente. Me pregunto si esto podría ser un error de Apple. O quizás esto se deba a una acumulación de inserciones o algo similar.
Seguimiento : he estado usando esta respuesta a continuación por Nick durante más de 2 años sin ningún problema (en caso de que la gente se pregunte si hay algún agujero en esa respuesta, todavía no he encontrado ninguno). Bien hecho Nick.
fuente
Pon esto en el viewController que posee la vista de colección
- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; [self.collectionView.collectionViewLayout invalidateLayout]; }
fuente
invalidateLayout
solo una vez.Descubrí problemas similares en la aplicación de mi iPhone. Buscar en el foro de desarrollo de Apple me trajo esta solución adecuada que funcionó en mi caso y probablemente también en el suyo:
Subclase
UICollectionViewFlowLayout
y anularshouldInvalidateLayoutForBoundsChange
para regresarYES
.//.h @interface MainLayout : UICollectionViewFlowLayout @end
y
//.m #import "MainLayout.h" @implementation MainLayout -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } @end
fuente
Una versión rápida de la respuesta de Nick Snyder:
class NDCollectionViewFlowLayout : UICollectionViewFlowLayout { override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let attributes = super.layoutAttributesForElements(in: rect) let contentSize = collectionViewContentSize return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height } } }
fuente
También he tenido este problema para un diseño de vista de cuadrícula básico con inserciones para márgenes. La depuración limitada que he hecho por ahora se está implementando
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
en mi subclase UICollectionViewFlowLayout y registrando lo que devuelve la implementación de la superclase, lo que muestra claramente el problema.- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attrsList = [super layoutAttributesForElementsInRect:rect]; for (UICollectionViewLayoutAttributes *attrs in attrsList) { NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y); } return attrsList; }
Al implementar
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
, también puedo ver que parece devolver los valores incorrectos para itemIndexPath.item == 30, que es el factor 10 de la cantidad de celdas por línea de mi vista de cuadrícula, no estoy seguro de si eso es relevante.- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item); return attrs; }
Con la falta de tiempo para más depuración, la solución que he hecho por ahora es reducir el ancho de mi colección de vistas en una cantidad igual al margen izquierdo y derecho. Tengo un encabezado que todavía necesita el ancho completo, así que configuré clipsToBounds = NO en mi vista de colección y luego también eliminé las inserciones izquierda y derecha en él, parece funcionar. Para que la vista de encabezado permanezca en su lugar, debe implementar el cambio de marco y el tamaño en los métodos de diseño que tienen la tarea de devolver layoutAttributes para la vista de encabezado.
fuente
He agregado un informe de error a Apple. Lo que me funciona es establecer la sección inferior Inset en un valor menor que la inserción superior.
fuente
Estaba experimentando el mismo problema de reemplazo de celda en el iPhone usando un
UICollectionViewFlowLayout
por lo que me alegré de encontrar tu publicación. Sé que tienes el problema en un iPad, pero estoy publicando esto porque creo que es un problema general conUICollectionView
. Así que esto es lo que descubrí.Puedo confirmar que el
sectionInset
es relevante para ese problema. Además de eso,headerReferenceSize
también influye si una celda se desplaza o no. (Esto tiene sentido ya que es necesario para calcular el origen).Desafortunadamente, incluso los diferentes tamaños de pantalla deben tenerse en cuenta. Al jugar con los valores de estas dos propiedades, experimenté que cierta configuración funcionaba en ambos (3,5 "y 4"), en ninguna o sólo en uno de los tamaños de pantalla. Generalmente ninguno de ellos. (Esto también tiene sentido, ya que los límites del
UICollectionView
cambios, por lo tanto, no experimenté ninguna disparidad entre retina y no retina).Terminé configurando el
sectionInset
yheaderReferenceSize
dependiendo del tamaño de la pantalla. Probé alrededor de 50 combinaciones hasta que encontré valores por debajo de los cuales el problema ya no ocurría y el diseño era visualmente aceptable. Es muy difícil encontrar valores que funcionen en ambos tamaños de pantalla.Entonces, resumiendo, solo puedo recomendarle que juegue con los valores, verifique estos en diferentes tamaños de pantalla y espero que Apple solucione este problema.
fuente
Acabo de encontrar un problema similar con las celdas que desaparecen después del desplazamiento de UICollectionView en iOS 10 (no tuve problemas en iOS 6-9).
Subclasificación de UICollectionViewFlowLayout y método de anulación layoutAttributesForElementsInRect: no funciona en mi caso.
La solución fue bastante simple. Actualmente utilizo una instancia de UICollectionViewFlowLayout y configuro tanto itemSize como estimadoItemSize (no estimadaItemSize antes) y lo configuré en un tamaño distinto de cero. El tamaño real se calcula en el método collectionView: layout: sizeForItemAtIndexPath:.
Además, eliminé una llamada al método invalidateLayout de layoutSubviews para evitar recargas innecesarias.
fuente
Acabo de experimentar un problema similar pero encontré una solución muy diferente.
Estoy usando una implementación personalizada de UICollectionViewFlowLayout con un desplazamiento horizontal. También estoy creando ubicaciones de marcos personalizados para cada celda.
El problema que estaba teniendo era que [super layoutAttributesForElementsInRect: rect] en realidad no devolvía todos los UICollectionViewLayoutAttributes que deberían mostrarse en la pantalla. En las llamadas a [self.collectionView reloadData], algunas de las celdas de repente se establecerían como ocultas.
Lo que terminé haciendo fue crear un NSMutableDictionary que almacenó en caché todos los UICollectionViewLayoutAttributes que he visto hasta ahora y luego incluir los elementos que sé que deberían mostrarse.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect]; NSMutableArray * attrs = [NSMutableArray array]; CGSize calculatedSize = [self calculatedItemSize]; [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) { NSIndexPath * idxPath = attr.indexPath; CGRect itemFrame = [self frameForItemAtIndexPath:idxPath]; if (CGRectIntersectsRect(itemFrame, rect)) { attr = [self layoutAttributesForItemAtIndexPath:idxPath]; [self.savedAttributesDict addAttribute:attr]; } }]; // We have to do this because there is a bug in the collection view where it won't correctly return all of the on screen cells. [self.savedAttributesDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSArray * cachedAttributes, BOOL *stop) { CGFloat columnX = [key floatValue]; CGFloat leftExtreme = columnX; // This is the left edge of the element (I'm using horizontal scrolling) CGFloat rightExtreme = columnX + calculatedSize.width; // This is the right edge of the element (I'm using horizontal scrolling) if (leftExtreme <= (rect.origin.x + rect.size.width) || rightExtreme >= rect.origin.x) { for (UICollectionViewLayoutAttributes * attr in cachedAttributes) { [attrs addObject:attr]; } } }]; return attrs; }
Aquí está la categoría para NSMutableDictionary que los UICollectionViewLayoutAttributes se están guardando correctamente.
#import "NSMutableDictionary+CDBCollectionViewAttributesCache.h" @implementation NSMutableDictionary (CDBCollectionViewAttributesCache) - (void)addAttribute:(UICollectionViewLayoutAttributes*)attribute { NSString *key = [self keyForAttribute:attribute]; if (key) { if (![self objectForKey:key]) { NSMutableArray *array = [NSMutableArray new]; [array addObject:attribute]; [self setObject:array forKey:key]; } else { __block BOOL alreadyExists = NO; NSMutableArray *array = [self objectForKey:key]; [array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *existingAttr, NSUInteger idx, BOOL *stop) { if ([existingAttr.indexPath compare:attribute.indexPath] == NSOrderedSame) { alreadyExists = YES; *stop = YES; } }]; if (!alreadyExists) { [array addObject:attribute]; } } } else { DDLogError(@"%@", [CDKError errorWithMessage:[NSString stringWithFormat:@"Invalid UICollectionVeiwLayoutAttributes passed to category extension"] code:CDKErrorInvalidParams]); } } - (NSArray*)attributesForColumn:(NSUInteger)column { return [self objectForKey:[NSString stringWithFormat:@"%ld", column]]; } - (void)removeAttributesForColumn:(NSUInteger)column { [self removeObjectForKey:[NSString stringWithFormat:@"%ld", column]]; } - (NSString*)keyForAttribute:(UICollectionViewLayoutAttributes*)attribute { if (attribute) { NSInteger column = (NSInteger)attribute.frame.origin.x; return [NSString stringWithFormat:@"%ld", column]; } return nil; } @end
fuente
Las respuestas anteriores no me funcionan, pero después de descargar las imágenes, reemplacé
con
[self.yourCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
para actualizar y puede mostrar todas las celdas correctamente, puede intentarlo.
fuente
Esto puede ser un poco tarde, pero asegúrese de establecer sus atributos en
prepare()
si es posible.Mi problema era que las celdas se estaban distribuyendo y luego se actualizaban
layoutAttributesForElements
. Esto resultó en un efecto de parpadeo cuando aparecieron nuevas celdas.Al mover toda la lógica de atributos a
prepare
, luego establecerlos enUICollectionViewCell.apply()
ella, se eliminó el parpadeo y se creó la visualización de celdas suaves de mantequilla 😊fuente