ios toolbar
Error de animación de diseño de flujo al insertar múltiples vistas suplementarias personalizadas (2)
A continuación se muestra el resultado del elemento CustomCollectionViewController
indexPath. Fila es para cellForItemAt indexPath
y Row Supplementary es para viewForSupplementaryElementOfKind
.
Como puede ver, cellForItemAt
devuelve correctamente el indexpath.item
pero en el viewForSupplementaryElementOfKind
no devuelve la respuesta esperada. No entendí el motivo hasta ahora, si recibo te actualizo pronto.
Por lo tanto, para volver a resolver, puede tratar con la matriz agregando nuevos datos en ella y volver a cargar collectionView en lugar de indexPath.
Al insertar múltiples vistas suplementarias personalizadas en una vista de colección con vistas suplementarias existentes, usando una subclase de UICollectionViewFlowLayout , la vista de colección parece crear animaciones desordenadas o no manejar las inserciones correctamente.
La inserción de una vista suplementaria se comporta como se esperaba, pero la inserción de dos vistas suplementarias a la vez da como resultado un problema visual obvio, y la inserción de muchas vistas adicionales a la vez (por ejemplo, 4+) empeora el problema. (iOS 10.2, iOS 9.3, Swift 3, Xcode 8.2.1)
Aquí hay una demostración:
Puede reproducir el problema utilizando este proyecto de muestra . En este ejemplo simplificado, hay una vista suplementaria para cada elemento, y dos elementos (con dos vistas suplementarias) se insertan durante cada actualización de lote. En mi proyecto real, tengo menos vistas suplementarias que elementos y utilizo otras características de diseño de flujo.
Mi mejor suposición es que algo está causando que la vista de recopilación confunda las rutas de índice o los atributos de diseño correspondientes a las vistas suplementarias existentes / insertadas. Si esto resulta ser un error en una de las clases de Apple, le estaría muy agradecido por su solución más elegante.
Soluciones intentadas
He intentado y verificado dos veces cada uno de los siguientes:
Implementando
indexPathsToInsertForSupplementaryView(ofKind:)
lo que puedo decir, esto es sencillo y mi implementación devuelve las rutas de índice correctas. Las vistas siempre están insertadas. Para las vistas de encabezado y pie de página complementarios, UICollectionViewFlowLayout parece devolver una matriz ordenada de rutas de índice, pero la ordenación de la matriz no ha hecho ninguna diferencia para mí.Proporcionar atributos de diseño inicial y final -llamar
super
para estos métodos parece devolver los marcos correctos, por lo que no es obvio qué ajustes (si los hubiera) se podrían hacer a los atributos de diseño inicial o final para resolver el problema. Pero vea la tercera curiosidad que se menciona a continuación.Invalidar el diseño: parece no hacer ninguna diferencia en absoluto si el diseño se invalida manualmente en su totalidad o en parte antes, durante o después de
performBatchUpdates()
, o en absoluto.Vías de índice personalizadas, aunque las rutas de índice para elementos suplementarios no necesitan corresponder con rutas de índice de elementos, hasta donde puedo decir, y a pesar de la documentación en contrario, UICollectionViewFlowLayout se bloqueará (solicitando atributos de diseño para rutas de índice inexistentes) a menos que los elementos suplementarios del mismo tipo se numeran secuencialmente de 0. Supongo que así es como la vista de colección y / o el objeto de disposición puede calcular nuevas rutas de índice cuando los elementos se insertan o eliminan (es decir, incrementando o disminuyendo la propiedad del
item
del ruta de índice). Entonces, construir rutas de índice arbitrarias no es una opción.
Curiosidades notadas durante la depuración
Las fallas visuales son específicas de las vistas suplementarias y no ocurren para los elementos insertados, incluso cuando las rutas de índice y los marcos son los mismos para ambos.
La depuración de la jerarquía de vista revela que, aunque los marcos de las vistas suplementarias insertadas son correctos, los marcos de algunas vistas suplementarias existentes no son correctos. Eso es así a pesar del hecho de que las tramas devueltas por el objeto de disposición para las vistas existentes en esas rutas de índice parecen ser correctas.
Al insertar múltiples vistas suplementarias, las llamadas realizadas por la vista de colección para los atributos de diseño inicial y final parecen estar desequilibradas. Es decir, los atributos de diseño inicial se solicitan varias veces para la misma ruta de índice (y, naturalmente, los mismos atributos se devuelven cada vez), y hay menos solicitudes de atributos de diseño final. Además, para algunas vistas suplementarias existentes, los atributos de disposición inicial y final nunca se solicitan en absoluto. Encuentro esto sospechoso. Me lleva a pensar que la vista de colección de alguna manera confunde vistas suplementarias y / o rutas de índice antes y después de la actualización.
Las direcciones de memoria para instancias de IndexPath creadas por mi código Swift son sorprendentemente diferentes a las instancias de NSIndexPath creadas internamente por UICollectionView y UICollectionViewFlowLayout. Los primeros siempre son diferentes (como se esperaba), mientras que los segundos parecen ser idénticos entre los lanzamientos (por ejemplo, [0, 0], [0, 1] y [0, 2] están siempre en
0xc000000000000016
,0xc000000000200016
y0xc000000000400016
). En Swift, IndexPath tiene un puente a NSIndexPath, pero el primero es un tipo de valor mientras que el segundo es un tipo de referencia . NSIndexPath también utiliza punteros etiquetados . Existe una posibilidad remota de que los dos se hayan puenteado de forma imperfecta y / o que las clases de vista de recopilación confíen internamente en el comportamiento de NSIndexPath de alguna manera que dé lugar a la confusión del camino de índice que se manifiesta aquí. No sé cómo probar esto o llevar esto más allá.
Preguntas similares
Las siguientes preguntas pueden estar relacionadas, aunque ninguna de las respuestas funcionó para mí:
¿Cómo se pueden insertar o eliminar correctamente las vistas suplementarias UICollectionView?
layoutAttributesForSupplementaryViewOfKind: atIndexPath: pasa en indexPath incorrecto
Las vistas suplementarias UICollectionView no se animarán "dentro" o "fuera"
La decoración de UICollectionView y las vistas suplementarias no se pueden mover
El problema también puede estar relacionado con este informe de error en Open Radar. Sin embargo, el proyecto de muestra que acompaña a ese informe es demasiado complicado como para ser de mucha utilidad.
Soporte técnico de Apple
Después de publicar esta pregunta, envié un incidente de soporte técnico a Apple. Apple Developer Support respondió de la siguiente manera:
Nuestros ingenieros han revisado su solicitud y han determinado que esto se trataría mejor como un informe de error.
Envíe un informe de error completo sobre este problema utilizando la herramienta de informe de errores en https://developer.apple.com/bug-reporting/ .
...
Hemos dedicado un tiempo a investigar la posibilidad de una solución alternativa y desafortunadamente hemos tenido las manos vacías. Por favor, sigue con tu error. Si encontramos la posibilidad de algún tipo de solución alternativa en el futuro, nos comunicaremos con usted. Lo siento, no tengo mejores noticias.
Si experimenta este problema, por favor duplique rdar://30510010 .
No estoy seguro de si tengo una solución correcta para el problema inicial, pero tuve el mismo problema que en la respuesta de @agent_stack.
Si tiene un diseño de vista de colección personalizada (subclase de UICollectionViewLayout o subclase de UICollectionViewFlowLayout) y agrega su propia vista suplementaria, debe sobrescribir un método adicional: indexPathsToInsertForSupplementaryView(ofKind elementKind: String)
La vista de colección llama a este método cada vez que agrega celdas o secciones a la vista de colección. La implementación de este método le da a su objeto de diseño la oportunidad de agregar nuevas vistas suplementarias para complementar las adiciones.
Hice mi propio CollectionViewFlowLayout y solo después de agregar estos métodos en combinación todo está animando como debería.
override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
super.prepare(forCollectionViewUpdates: updateItems)
// Prepare update by storing index path to insert
// ────────────────────────────────────────────────────────────
// IMPORTANT: Item vs. Section Update
//
// An instance of UICollectionViewUpdateItem can represent either an item or a section update,
// and the only way to tell which is that update items which represent section updates contain
// index paths with a valid section index and NSNotFound for the item index, while update items
// that represent item updates contain index paths with valid item indexes.
//
// This appears to be completely undocumented by Apple, but it is absolutely consistent behavior.
// https://www.raizlabs.com/blog/2013/10/animating_items_uicollectionview/
var indexPaths = [IndexPath]()
for updateItem in updateItems {
switch updateItem.updateAction {
case .insert:
// If it is a section update then convert NSNotFound to zero.
// In our case a section update always has only the first item.
if var indexPath = updateItem.indexPathAfterUpdate {
if indexPath.item == NSNotFound { indexPath.item = 0 }
indexPaths.append(indexPath)
}
default:
break
}
}
indexPathsToInsert = indexPaths
}
override func indexPathsToInsertForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {
// Extra add supplementary index paths
// ────────────────────────────────────────────────────────────
// The collection view calls this method whenever you add cells or sections to the collection view.
// Implementing this method gives your layout object an opportunity to add new supplementary views
// to complement the additions.
// http://.com/a/38289843/7441991
switch elementKind {
case UICollectionElementKindDateSeparator:
return indexPathsToInsert
case UICollectionElementKindAvatar:
return indexPathsToInsert
default:
// If it is not a custom supplementary view, return attributes from flow layout
return super.indexPathsToInsertForSupplementaryView(ofKind: elementKind)
}
}
¡No dude en ponerse en contacto conmigo, si tiene alguna pregunta! :)