ios - example - uicollectionview swift 4 tutorial
UICollectionView performance-_updateVisibleCellsNow (11)
¡Un gran pulgar hacia las dos respuestas de arriba! Aquí hay algo más que puedes probar: he tenido grandes mejoras en el rendimiento de UICollectionView
al deshabilitar el diseño automático. Si bien tendrá que agregar un código adicional para diseñar los interiores de la celda, el código personalizado parece ser tremendamente más rápido que el diseño automático.
Estoy trabajando en un UICollectionViewLayout
personalizado que muestra las celdas organizadas por día / semana / mes.
No se desplaza suavemente, y parece que el retardo es causado por [UICollectionView _updateVisibleCellsNow]
se llama en cada ciclo de representación.
El rendimiento está bien para <30 elementos, pero a alrededor de 100 o más, es terriblemente lento. ¿Es esto una limitación de UICollectionView y diseños personalizados, o no estoy dando a la vista suficiente información para realizar correctamente?
Fuente aquí: https://github.com/oskarhagberg/calendarcollection
Fuente de datos y delegado: https://github.com/oskarhagberg/calendarcollection/blob/master/CalendarHeatMap/OHCalendarView.m
Perfil de tiempo:
Actualizar
Tal vez es inútil? Algunas pruebas con un UICollectionViewController
simple con un UICollectionViewFlowLayout
que tiene aproximadamente la misma cantidad de células / resultados de pantalla en un perfil de tiempo similar.
Siento que debería ser capaz de manejar ~ 100 células opacas simples a la vez sin la trepidación. ¿Me equivoco?
Además de las respuestas enumeradas (rasterizar, diseño automático, etc.), es posible que también desee verificar otras razones que potencialmente reduzcan el rendimiento.
En mi caso, cada uno de mis UICollectionViewCell contiene otro UICollectionView (con aproximadamente 25 celdas cada uno), cuando cada una de las celdas se está cargando, llamo al UICollectionView.reloadData interno (), lo que significativamente reduce el rendimiento.
Luego puse el reloadData dentro de la cola de IU principal, el problema se ha ido:
DispatchQueue.main.async {
self.innerCollectionView.reloadData()
}
También puede ayudar el hecho de buscar cuidadosamente razones como estas. ¡Gracias! :)
Además, no olvide intentar rasterizar la capa de la celda:
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
¡Tuve 10 fps sin eso, y auge 55 fps con! No estoy muy familiarizado con GPU y el modelo de composición, entonces, ¿qué hace exactamente no es del todo claro para mí, pero básicamente aplana la representación de todas las subvistas en un solo mapa de bits (en lugar de un mapa de bits por subvista?). De todos modos, no sé si tiene algunos inconvenientes, ¡pero es dramáticamente más rápido!
Al igual que @Altimac, también utilicé este código para resolver un problema con el desplazamiento de iPad en UICollectionView:
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
También tenía 6-10 fps, y ahora tengo 57 fps
Algunas observaciones más que pueden ser útiles:
Puedo reproducir el problema, usando el diseño de flujo con alrededor de 175 elementos visibles a la vez: se desplaza suavemente en el simulador pero se demora como el infierno en el iPhone 5. Asegúrate de que sean opacos, etc.
Lo que termina tomando más tiempo parece ser el trabajo con un NSDictionary mutable dentro de _updateVisibleCellsNow
. Copiando el diccionario, pero también buscando elementos por clave. Las claves parecen ser UICollectionViewItemKey y el [UICollectionViewItemKey isEqual:]
es el método que más tiempo consume. UICollectionViewItemKey contiene al menos las propiedades de type
, identifier
e indexPath
, y la propiedad contenida indexPath comparison [NSIndexPath isEqual:]
toma más tiempo.
De eso estoy adivinando que podría faltar la función hash de UICollectionViewItemKey ya que isEqual:
se llama con tanta frecuencia durante la búsqueda del diccionario. Muchos de los elementos pueden estar terminando con el mismo hash (o en el mismo cubo de hash, no estoy seguro de cómo funciona NSDictionary).
Por alguna razón, es más rápido con todos los elementos en 1 sección, en comparación con muchas secciones con 7 elementos en cada uno. Probablemente porque pasa tanto tiempo en NSIndexPath isEqual y eso es más rápido si la fila diffs primero, o tal vez que UICollectionViewItemKey obtiene un mejor hash.
Honestamente, se siente realmente extraño que UICollectionView haga que ese pesado diccionario funcione en cada cuadro de desplazamiento, como se mencionó antes, cada actualización de cuadro debe ser <16ms para evitar el retraso. Me pregunto si esa cantidad de búsquedas en el diccionario es:
- Realmente necesario para la operación general UICollectionView
- Hay para apoyar alguna funda lateral que rara vez se usa y que se puede desactivar para la mayoría de los diseños
- Algunos códigos internos no optimizados que aún no se han corregido
- Algunos errores de nuestro lado
Esperamos ver mejoras este verano durante la WWDC, o si alguien más puede encontrar la manera de solucionarlo.
Aquí está la respuesta de Altimac convertida a Swift 3:
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.main.scale
Además, debe tenerse en cuenta que este código va en su método delegado collectionView para cellForItemAtIndexPath.
Un consejo más: para ver los cuadros por segundo (FPS) de una aplicación, abre la animación principal en Instrumentos (ver imagen).
El problema no es la cantidad de celdas que se muestran en el total de la vista de recopilación, es la cantidad de celdas que están en la pantalla a la vez. Dado que el tamaño de la celda es muy pequeño (22x22), tiene 154 celdas visibles en la pantalla a la vez. Prestar cada uno de estos es lo que está desacelerando su interfaz. Puede probar esto aumentando el tamaño de la celda en su Storyboard y volviendo a ejecutar la aplicación.
Desafortunadamente, no hay mucho que puedas hacer. Recomiendo mitigar el problema al evitar el recorte de límites y tratar de no implementar drawRect:
ya que es lento.
En algunos casos, se debe a la disposición automática en UICollectionViewCell. Apágalo (si puedes vivir sin él) y el desplazamiento se volverá suave como la mantequilla :) Es un problema de iOS, que han resuelto desde edades.
He estado haciendo una investigación considerable del rendimiento de UICollectionView. La conclusión es simple. El rendimiento para una gran cantidad de células es pobre.
EDITAR: Disculpas, solo vuelve a leer tu publicación, la cantidad de celdas que tienes debe estar bien (mira el resto de mi comentario), por lo que la complejidad de las celdas también puede ser un problema.
Si su diseño lo admite, verifique:
Cada celda es opaca
Contenido de la celda clips a los límites.
Las posiciones de coordenadas de celda no contienen valores fraccionarios (por ejemplo, siempre calcule que sean píxeles enteros)
Intenta evitar la superposición de celdas.
Intenta evitar las sombras.
La razón de esto es bastante simple. Muchas personas no entienden esto, pero vale la pena entenderlo: UIScrollViews no utiliza Core Animation para desplazarse. Mi ingenua creencia era que involucraban una "salsa" de animación secreta de desplazamiento y simplemente solicitaban actualizaciones ocasionales de los delegados de vez en cuando para verificar el estado. Pero, de hecho, las vistas de desplazamiento son objetos que no aplican ningún comportamiento de dibujo en absoluto. Todo lo que realmente son es una clase que aplica una función matemática que abstrae la ubicación de coordenadas de las UIViews que contienen, por lo que las coordenadas de Vistas se tratan como relativas a un plano abstracto de contenido en lugar de referirse al origen de la vista que lo contiene. Una vista de desplazamiento actualizará la posición del plano de desplazamiento abstracto de acuerdo con la entrada del usuario (por ejemplo, deslizar) y, por supuesto, también hay un algoritmo de física que proporciona "momento de movimiento" a las posiciones de coordenadas traducidas.
Ahora bien, si tuvieras que producir tu propio objeto de diseño de vista de colección, en teoría, podrías producir uno que invierte al 100% la traducción matemática aplicada por la vista de desplazamiento subyacente. Esto sería interesante pero inútil, porque luego parecería que las células no se mueven del todo mientras deslizas. Pero planteo esta posibilidad porque ilustra que el objeto de disposición de vista de colección que trabaja con el objeto de vista de colección hace una operación muy similar a la vista de desplazamiento subyacente. Por ejemplo, simplemente brinda la oportunidad de aplicar una traducción matemática cuadro por cuadro adicional de los atributos de las vistas que se mostrarán y, en general, se tratará simplemente de una traducción de atributos de posición.
Solo cuando se insertan o eliminan nuevas celdas, o se vuelve a cargar, CoreAnimation está involucrado; más usualmente llamando:
- (void)performBatchUpdates:(void (^)(void))updates
completion:(void (^)(BOOL finished))completion
UICollectionView solicita disposición de celdaAttributes para cada cuadro de desplazamiento y cada vista visible se presenta para cada cuadro. Los UIView son objetos ricos optimizados para mayor flexibilidad que para rendimiento. Cada vez que se distribuye uno, hay una serie de pruebas que el sistema realiza para verificar su alfa, zndice, subvista, atributos de recorte, etc. La lista es larga. Estas comprobaciones y cualquier cambio resultante en la vista se llevan a cabo para cada elemento de vista de colección para cada fotograma.
Para garantizar un buen rendimiento, todas las operaciones de cuadro por cuadro deben completarse en 17 ms. [Con el número de células que tienes, eso simplemente no va a suceder] puso entre corchetes esta cláusula porque he vuelto a leer tu publicación y me doy cuenta de que la he leído mal. Con la cantidad de células que tienes, no debería haber un problema de rendimiento. Personalmente, encontré con una prueba simplificada con células de vainilla, que contiene solo una imagen en caché, el límite probado en un iPad 3 es de aproximadamente 784 células en pantalla antes de que el rendimiento comience a caer por debajo de 50 fps.
En la práctica, necesitarás mantenerlo a menos de esto.
Personalmente estoy usando mi propio objeto de diseño personalizado y necesito un rendimiento superior al que ofrece UICollectionView. Lamentablemente, no ejecuté la prueba simple anterior hasta un punto de la ruta de desarrollo y me di cuenta de que hay problemas de rendimiento. Estoy así que voy a volver a trabajar en el puerto de acceso abierto de UICollectionView, PSTCollectionView. Creo que hay una solución que se puede implementar, por lo que, solo para el desplazamiento general, la capa de cada elemento de la celda se escribe con una operación que elude el diseño de cada celda UIView. Esto funcionará para mí ya que tengo mi propio objeto de diseño, y sé cuándo es necesario el diseño y tengo un buen truco que permitirá que PSTCollectionView vuelva a su modo normal de operación en este momento. He revisado la secuencia de llamadas y no parece ser demasiado compleja, ni tampoco parece inviable. Pero con seguridad no es trivial y se deben realizar más pruebas antes de poder confirmar que funcionará.
Pensé que rápidamente daría mi solución, ya que me enfrenté a un problema muy similar: UICollectionView basado en imágenes.
En el proyecto en el que estaba trabajando, buscaba imágenes en la red, las guardaba en la memoria caché localmente en el dispositivo y luego volvía a cargar la imagen en caché durante el desplazamiento.
Mi error fue que no estaba cargando imágenes en caché en un hilo de fondo.
Una vez que coloqué mi [UIImage imageWithContentsOfFile:imageLocation];
en un hilo de fondo (y luego lo apliqué a mi imageView a través de mi hilo principal), mi FPS y el desplazamiento fueron mucho mejores.
Si aún no lo has probado, definitivamente pruébalo.
Si está implementando un diseño de cuadrícula, puede UICollectionViewCell
este problema utilizando un solo UICollectionViewCell
para cada row
y agregando UIView
''s anidados a la cell
. De hecho, UICollectionViewCell
y UICollectionReusableView
y prepareForReuse
método prepareForReuse
para eliminar todas las subvistas. En collectionView:cellForItemAtIndexPath:
agrego en todas las subviews
que originalmente eran celdas que configuran su marco a la coordenada x
usada en la implementación original, pero ajustando su coordenada y
para estar dentro de la cell
. Usando este método, pude seguir usando algunas de las sutilezas de UICollectionView
, como targetContentOffsetForProposedContentOffset:withScrollingVelocity:
para alinear bien en los lados superior e izquierdo de una celda. Pasé de tener 4-6 FPS a 60 FPS sin problemas .