objective framework data ios objective-c cocoa-touch core-animation

ios - framework - drawViewHierarchyInRect: afterScreenUpdates: retrasa otras animaciones



objective c documentation (4)

En mi aplicación, uso drawViewHierarchyInRect:afterScreenUpdates: para obtener una imagen borrosa de mi vista (usando la categoría UIImage de Apple UIImageEffects ).

Mi código se ve así:

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0); [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; UIImage *im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); /* Use im */

Noté durante el desarrollo que muchas de mis animaciones se retrasaron después de usar mi aplicación por un momento, es decir, mis vistas empezaron a tener sus animaciones después de una pausa notable (pero de menos de aproximadamente un segundo) en comparación con un nuevo lanzamiento de la aplicación.

Después de una cierta depuración, noté que el mero hecho de usar drawViewHierarchyInRect:afterScreenUpdates: con las actualizaciones de pantalla configuradas en YES causó este retraso. Si este mensaje nunca se envió durante una sesión de uso, el retraso nunca apareció. El uso de NO para el parámetro de actualizaciones de pantalla también hizo desaparecer el retraso.

Lo extraño es que este código borroso no tiene ninguna relación (por lo que puedo decir) con las animaciones retrasadas. Las animaciones en cuestión no usan drawViewHierarchyInRect:afterScreenUpdates: son CAKeyframeAnimation animación de CAKeyframeAnimation fotograma. El mero hecho de enviar este mensaje (con las actualizaciones de pantalla establecidas en YES ) parece tener animaciones afectadas globalmente en mi aplicación.

¿Que esta pasando?

(He creado videos que ilustran el efecto: with y without un retraso de animación. Tenga en cuenta el retraso en la aparición del bocadillo de diálogo "¡Revisar!" En la barra de navegación.)

ACTUALIZAR

He creado un proyecto de ejemplo para ilustrar este error potencial. https://github.com/timarnold/AnimationBugExample

ACTUALIZACIÓN No. 2

Recibí una respuesta de Apple verificando que esto es un error. Ver respuesta a below .


¿Has intentado ejecutar tu código en un hilo de fondo? Aquí hay un ejemplo usando gcd:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ //background thread UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0); [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; UIImage *im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_sync(dispatch_get_main_queue(), ^(void) { //update ui on main thread }); });


¿Por qué tiene esta línea (de su aplicación de muestra):

animation.beginTime = CACurrentMediaTime();

Solo quítalo, y todo será como quieras que sea.

Al establecer el tiempo de animación explícitamente en CACurrentMediaTime() , ignora las posibles transformaciones de tiempo que pueden estar presentes en el árbol de capas. O bien no lo configure (las animaciones por defecto comenzarán now ) o use el método de conversión de tiempo:

animation.beginTime = [view.layer convert​Time:CACurrentMediaTime() from​Layer:nil];

UIKit agrega transformaciones de tiempo al árbol de capas cuando llama después de afterScreenUpdates:YES para evitar saltos en la animación en curso, que serían causados ​​de otra manera por las confirmaciones intermedias de CoreAnimation. Si desea iniciar la animación en un momento específico (no now ), utilice el método de conversión de tiempo mencionado anteriormente.


Cuando el parámetro afterScreenUpdates se establece en SÍ, el sistema tiene que esperar hasta que todas las actualizaciones de pantalla pendientes hayan ocurrido antes de poder representar la vista.

Si está activando animaciones al mismo tiempo, tal vez la representación y las animaciones intenten suceder juntas y esto está causando un retraso.

Puede que valga la pena experimentar con el lanzamiento de tus animaciones un poco más tarde para evitar esto. Obviamente, no mucho más tarde porque eso anularía el objeto, pero valdría la pena intentar un pequeño intervalo dispatch_after.


Usé uno de mis tickets de soporte para desarrolladores de Apple para preguntarle a Apple sobre mi problema.

Resulta que es un error confirmado (número de radar 17851775). Su hipótesis para lo que está sucediendo está abajo:

El método drawViewHierarchyInRect: afterScreenUpdates: realiza sus operaciones en la GPU tanto como sea posible, y gran parte de este trabajo probablemente suceda fuera del espacio de direcciones de la aplicación en otro proceso. Al pasar SÍ como el parámetro afterScreenUpdates: a drawViewHierarchyInRect: afterScreenUpdates: la Animación Core eliminará todos sus búferes en su tarea y en la tarea de renderizado. Como puede imaginar, también hay muchas otras cosas internas que ocurren en estos casos. La ingeniería teoriza que puede ser un error en esta maquinaria relacionada con el efecto que está viendo.

En comparación, el método renderInContext: realiza sus operaciones dentro del espacio de direcciones de la aplicación y no utiliza el proceso basado en GPU para realizar el trabajo. En su mayor parte, esta es una ruta de código diferente y si está funcionando para usted, entonces esa es una solución adecuada. Esta ruta no es tan eficiente como no utiliza la tarea basada en GPU. Además, no es tan preciso para las capturas de pantalla, ya que puede excluir desenfoques y otras funciones de animación central que son gestionadas por la tarea de GPU.

Y también proporcionaron una solución. Ellos sugirieron que en lugar de:

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0); [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; UIImage *im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); /* Use im */

Debería hacer esto

UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0); [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *im = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); /* Use im */

Esperemos que esto sea útil para alguien!