uicollectionviewcontroller - uicollectionviewcell indexpath
Excepción de UICollectionView en UICollectionViewLayoutAttributes from iOS7 (6)
iOS 10
En iOS 10, se introduce una nueva característica, es Cell Prefetching
. Permitirá que la posición dinámica de SupplementaryView
bloquee. Para ejecutarse en el comportamiento anterior, debe deshabilitar prefetchingEnabled
. Es true
por defecto en iOS 10.
// Obj-C
// This function is available in iOS 10. Disable it for dynamic position of `SupplementaryView `.
if ([self.collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) {
self.collectionView.prefetchingEnabled = false;
}
// Swift
if #available(iOS 10, *) {
// Thanks @maksa
collectionView.prefetchingEnabled = false
// Swift 3 style
colView.isPrefetchingEnabled = false
}
Odio este problema. Pasé 2 días por este problema. Una referencia sobre Cell Pre-Fetch @iOS 10 .
iOS 9 y antes ...
@Away Lin tiene razón. . Resuelvo el mismo problema implementando ese método de delegado.
My Custom UICollectionViewLayout
modificará los atributos en layoutAttributesForElementsInRect
. La posición de la sección es dinámica, no estática. Por lo tanto, obtengo advertencias sobre los layout attributes for supplementary item at index path ... changed from ... to ...
Antes de los cambios, los métodos relacionados con invalideLayout
deben ser llamados.
Y, después de implementar este método delegado para devolver true
, se llamará al método invalidateLayoutWithContext:
al desplazarse por UICollectionViewLayout
. Por defecto, devuelve false
.
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
De Apple Docs
Devolver valor verdadero si la vista de colección requiere una actualización de diseño o falso si no es necesario cambiar el diseño.
Discusión La implementación por defecto de este método devuelve falso. Las subclases pueden anularlo y devolver un valor apropiado en función de si los cambios en los límites de la vista de colección requieren cambios en el diseño de las celdas y en las vistas complementarias.
Si los límites de la vista de colección cambian y este método devuelve verdadero, la vista de colección invalida el diseño llamando al método invalidateLayoutWithContext :.
Disponibilidad disponible en iOS 6.0 y versiones posteriores.
Y más ...
Un buen proyecto de ejemplo en GitHub, para UICollectionViewLayout personalizado.
He hecho una vista en CollectionView con CustomLayout. En iOS6 funcionó muy bien, pero iOS7 lanza una excepción como esta.
Aplicación de terminación debido a la excepción no detectada ''NSInternalInconsistencyException'', razón:
''atributos de diseño para el elemento suplementario en la ruta del índice ({longitud = 2, ruta = 0 - 0}) cambiado de CustomSupplementaryAttributes: 0xd1123a0 ruta del índice: (NSIndexPath: 0xd112580 {longitud = 2, ruta = 0 - 0}); tipo de elemento: (identificador); marco = (0 0; 1135.66 45); índice z = -1; a CustomSupplementaryAttributes: 0xd583c80 index path: (NSIndexPath: 0xd583c70 {length = 2, path = 0 - 0}); tipo de elemento: (identificador); marco = (0 0; 1135.66 45); índice z = -1; sin invalidar el diseño ''
Debe invalidar el diseño existente antes de actualizar, vea el final del mensaje de error:
sin invalidar el diseño ''
[collectionViewLayout invalidateLayout];
No estoy completamente seguro de cómo o por qué, pero parece que esto está solucionado en iOS 12, que admite tanto el cambio de tamaño de vista adicional como el de captura previa. El truco para mí era asegurarse de que las cosas sucedieran en el orden correcto.
Aquí hay una implementación de trabajo de una vista de encabezado extensible. Observe la implementación del cambio de tamaño del encabezado en layoutAttributesForElements(in rect: CGRect)
:
class StretchyHeaderLayout: UICollectionViewFlowLayout {
var cache = [UICollectionViewLayoutAttributes]()
override func prepare() {
super.prepare()
cache.removeAll()
guard let collectionView = collectionView else { return }
let sections = [Int](0..<collectionView.numberOfSections)
for section in sections {
let items = [Int](0..<collectionView.numberOfItems(inSection: section))
for item in items {
let indexPath = IndexPath(item: item, section: section)
if let attribute = layoutAttributesForItem(at: indexPath) {
cache.append(attribute)
}
}
}
if let header = layoutAttributesForSupplementaryView(ofKind: StretchyCollectionHeaderKind, at: IndexPath(item: 0, section: 0)) {
cache.append(header)
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let visibleAttributes = cache.filter { rect.contains($0.frame) || rect.intersects($0.frame) }
guard let collectionView = collectionView else { return visibleAttributes }
// Find the header and stretch it while scrolling.
guard let header = visibleAttributes.filter({ $0.representedElementKind == StretchyCollectionHeaderKind }).first else { return visibleAttributes }
header.frame.origin.y = collectionView.contentOffset.y
header.frame.size.height = headerHeight.home - collectionView.contentOffset.y
header.frame.size.width = collectionView.frame.size.width
return visibleAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = super.layoutAttributesForItem(at: indexPath as IndexPath)?.copy() as! UICollectionViewLayoutAttributes
guard collectionView != nil else { return attributes }
attributes.frame.origin.y = headerHeight.home + attributes.frame.origin.y
return attributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: StretchyCollectionHeaderKind, with: indexPath)
}
override var collectionViewContentSize: CGSize {
get {
guard let collectionView = collectionView else { return .zero }
let numberOfSections = collectionView.numberOfSections
let lastSection = numberOfSections - 1
let numberOfItems = collectionView.numberOfItems(inSection: lastSection)
let lastItem = numberOfItems - 1
guard let lastCell = layoutAttributesForItem(at: IndexPath(item: lastItem, section: lastSection)) else { return .zero }
return CGSize(width: collectionView.frame.width, height: lastCell.frame.maxY + sectionInset.bottom)
}
}
}
PD: Estoy consciente de que el caché no sirve para nada en este punto :)
Resolví mi problema anulando el método en la subcláusula de UICollectionViewFlowLayout
:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
volver SÍ
También tuve este problema, porque tenía un código que dependía del tamaño del contenido de la vista de colección. Mi código estaba accediendo al tamaño del contenido a través de collectionView!.contentSize
lugar de collectionViewContentSize
.
El primero usa la propiedad collectionView
de UICollectionViewLayout
, mientras que el segundo usa la propiedad de diseño implementado a medida. En mi código, la primera vez que se solicitó el diseño de los atributos, no se había establecido contentSize
todavía.
Tuve la misma excepción: en iOS 7, ahora necesita anular la heredada isEqual:
en su subclase UICollectionViewLayoutAttributes como se indica en la documentación de Apple here .