objective-c - microsoft - visual studio installer
¿El momento exacto en que iOS toma la instantánea de la vista al ingresar en segundo plano? (5)
Ahora he realizado algunas pruebas y he eliminado el problema, gracias a @Fabian Kreiser.
Para concluir: Kreiser lo hizo bien: iOS toma la captura de pantalla inmediatamente después del método applicationDidEnterBackground: devuelve - lo que significa inmediatamente, antes del final del runloop actual .
Lo que esto significa es que, si inicia cualquier tarea programada en el método didEnterBackground que desea terminar antes de salir, deberá dejar que el runloop actual se ejecute durante el tiempo que las tareas puedan tardar en finalizar.
En mi caso, la tarea programada fue una llamada al método UIAnimateWithDuration. Me dejé confundir por el hecho de que tanto su retraso como su duración fueron de 0. Sin embargo, la llamada estaba programada para ejecutarse en otro hilo y, por lo tanto, no pudo Finalizar antes del final del método applicationDidEnterBackground. Resultado: la captura de pantalla se tomó antes de que la pantalla se actualizara al estado que quería y, al relanzarla, esta captura de pantalla apareció brevemente en la pantalla, causando el parpadeo no deseado.
Además, para proporcionar el comportamiento de transición "suave" vs. "instantáneo" explicado en mi pregunta, la sugerencia de Kreiser de retrasar la llamada de transición "suave" en applicationWillResignActive: y cancelar la llamada en applicationDidEnterBackground: funciona bien. Noté que el retraso entre los dos métodos de delegado fue de alrededor de 0.005-0.019 segundos en mi caso, así que apliqué un margen generoso y utilicé un retraso de 0.05 segundos.
Mi recompensa, la respuesta correcta, y mi agradecimiento a Fabián. Esperemos que esto ayude a otros en situaciones similares, también.
Tengo un problema al poner en segundo plano la aplicación de mi iPhone presionando el botón de salida y luego reiniciando presionando el ícono de inicio en la pantalla de inicio: la vista de la aplicación vuelve a su estado inicial como quiero, pero antes de eso parpadea el estado de vista anterior, incorrecto en pantalla brevemente.
Fondo
Mi vista principal consiste básicamente en una secuencia de llamadas UIAnimateWithDuration interconectadas. El comportamiento que deseo cada vez que se produce una interrupción es restablecer la animación a su estado inicial (a menos que todas las animaciones hayan finalizado y la aplicación haya entrado en la fase final estática), y comenzar de nuevo cada vez que la aplicación vuelva al estado activo y visible. .
Después de estudiar el tema, aprendí que necesito dos tipos de código de manejo de interrupciones para proporcionar un buen ux: "instantáneo" y "suave". Tengo el método resetAnimation que restablece las propiedades de la vista al estado inicial al instante, y el método pauseAnimation que se anima rápidamente al mismo estado, con una etiqueta adicional que indica "pausado" desapareciendo en la parte superior de la vista.
Doble click en el botón de salida.
El motivo de esto es el caso de uso del "botón de salida con doble clic", que en realidad no oculta su vista ni lo pone en el estado de fondo, solo se desplaza un poco hacia arriba para mostrar el menú de multitarea en la parte inferior. Por lo tanto, restablecer el estado de la vista instantáneamente en este caso solo se veía muy feo. La transición animada y decirle al usuario que estás en pausa parecía una mejor idea.
Este caso funciona bien y de manera inteligente al implementar el método de delegado applicationWillResignActive en mi App Delegate y llamar a pauseAnimation desde allí. Manejo el retorno de ese menú multitarea al implementar el método delegado applicationDidBecomeActive y llamar desde allí mi método resumeAnimation, que se desvanece de la etiqueta "pausada" si está allí, y comienza mi secuencia de animación desde el estado inicial.
Todo esto funciona bien, sin parpadeo en cualquier lugar.
Visita al otro lado
Mi aplicación está construida sobre la plantilla de "utilidad" de Xcode, por lo que tiene una vista al revés para mostrar información / configuración. Me ocupo de visitar el lado opuesto y volver a la vista principal implementando estos dos métodos de delegado en mi controlador de vista principal:
(void) viewDidDisappear: (BOOL) animated
(void) viewDidAppear: (BOOL) animado
Llamo a mi resetAnimation en el método viewDidDisappear y resumeAnimation en viewDidAppear. Todo esto funciona bien, la vista principal es su estado inicial desde el comienzo de la transición al estado visible, sin destellos inesperados de estados de animación incorrectos de nada. Pero:
Presionando el botón de salida y relanzando desde el ícono de mi aplicación (¡la parte de buggy!)
Aquí es donde comienza el problema. Cuando presiono el botón de salir una vez y mi aplicación comienza su transición al fondo, suceden dos cosas. Primero, también se llama aquí a la aplicaciónWillResignActive, por lo que también se inicia el método pauseAnimation. No sería necesario, ya que la transición no tiene que ser suave aquí, la vista solo se vuelve estática y se "aleja" para revelar la pantalla de inicio, pero ¿qué puedes hacer? Bueno, tampoco haría ningún daño si pudiera llamar a resetAnimation antes del momento exacto en que el sistema tome la instantánea de la vista.
De todos modos, en segundo lugar, se llama a la aplicación DidEnterBackground en el delegado de la aplicación. Intenté llamar a resetAnimation desde allí para que la vista estuviera en el estado correcto cuando la aplicación regrese, pero esto no parece funcionar. Parece que la "instantánea" ya se tomó y, por lo tanto, cuando toco el ícono de inicio de mi aplicación y vuelvo a tocar, el estado de vista incorrecta parpadea brevemente en la pantalla antes de que aparezca el estado inicial correcto. Después de eso, funciona bien, las animaciones se desarrollan como se supone que deben hacerlo, pero ese parpadeo feo en ese momento de relanzamiento no desaparecerá, no importa lo que intente.
Fundamentalmente, lo que busco es qué momento exacto toma el sistema esta instantánea. Y, en consecuencia, ¿cuál sería el método correcto de delegado o el manejador de notificaciones para preparar mi opinión para tomar la "foto de recuerdo"?
PD. Luego está el default.png, que no parece mostrarse solo en el primer lanzamiento, sino también cuando el procesador tiene dificultades o regresa a la aplicación por algún otro motivo. Es un poco feo, especialmente si está regresando a su vista del lado opuesto que se ve totalmente diferente de su vista predeterminada. Pero esta es una característica fundamental de iOS, supongo que ni siquiera debería intentar averiguar o controlar esa :)
Edición: dado que la gente estaba pidiendo el código real, y mi aplicación ya se ha lanzado después de hacer esta pregunta, publicaré algo aquí. (La aplicación se llama Sweetest Kid, y si quieres ver cómo funciona realmente, está aquí: http://itunes.apple.com/app/sweetest-kid/id476637106?mt=8 )
Aquí está mi método de pausa de animación: resetAnimation es casi idéntico, excepto que su llamada de animación tiene una duración y demora de cero, y no muestra la etiqueta "Pausada". Una razón por la que estoy usando UIAnimation para restablecer los valores en lugar de simplemente asignar los nuevos valores es que, por alguna razón, las animaciones simplemente no se detuvieron si no usaba UIAnimation. De todos modos, aquí está el método pauseAnimation:
- (void)pauseAnimation {
if (currentAnimationPhase < 6 || currentAnimationPhase == 255) {
// 6 means finished, 255 is a short initial animation only showing at first launch
self.paused = YES;
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionAllowUserInteraction |
UIViewAnimationOptionBeginFromCurrentState |
UIViewAnimationOptionCurveEaseInOut |
UIViewAnimationOptionOverrideInheritedCurve |
UIViewAnimationOptionOverrideInheritedDuration
animations:^{
pausedView.alpha = 1.0;
cameraImageView.alpha = 0;
mirrorGlowView.alpha = 0;
infoButton.alpha = 1.0;
chantView.alpha = 0;
verseOneLabel.alpha = 1.0;
verseTwoLabel.alpha = 0;
verseThreeLabel.alpha = 0;
shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
shine1View.transform = CGAffineTransformIdentity;
stars1View.transform = CGAffineTransformIdentity;
stars2View.transform = CGAffineTransformIdentity;
finishedMenuView.alpha = 0;
preparingMagicView.alpha = 0;}
completion:^(BOOL finished){
pausedView.alpha = 1.0;
cameraImageView.alpha = 0;
mirrorGlowView.alpha = 0;
infoButton.alpha = 1.0;
chantView.alpha = 0;
verseOneLabel.alpha = 1.0;
verseTwoLabel.alpha = 0;
verseThreeLabel.alpha = 0;
shine1View.alpha = stars1View.alpha = stars2View.alpha = 0;
shine1View.transform = CGAffineTransformIdentity;
stars1View.transform = CGAffineTransformIdentity;
stars2View.transform = CGAffineTransformIdentity;
finishedMenuView.alpha = 0;
preparingMagicView.alpha = 0;
}];
askTheMirrorButton.enabled = YES;
againButton.enabled = NO;
shareOnFacebookButton.enabled = NO;
emailButton.enabled = NO;
saveButton.enabled = NO;
currentAnimationPhase = 0;
[[cameraImageView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; // To remove the video preview layer
}
}
Dependiendo de qué tan importante es para usted que esta transición se realice sin problemas, puede cancelar la multitarea para su aplicación por completo con UIApplicationExitsOnSuspend. Entonces, tendrías garantizado tu Default.png y un estado visual limpio.
Por supuesto, tendría que guardar / restaurar el estado al salir / iniciar, y sin más información sobre la naturaleza de su aplicación, es difícil decir si esto valdría la pena.
En iOS 7, hay una [[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch]
que hace exactamente lo que necesita.
La captura de pantalla se toma inmediatamente después de que este método vuelve. Supongo que su método -resetAnimation se completa en el siguiente ciclo de ejecución y no inmediatamente. No he intentado esto, pero puedes intentar dejar que el runloop se ejecute y luego regresar un poco más tarde:
- (void) applicationDidEnterBackground:(UIApplication *)application {
// YOUR CODE HERE
// Let the runloop run for a brief moment
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
Espero que esto ayude, fabian
Actualización: -pauseAnimation y -resetAnimation distinción
Enfoque: Retrase la animación que ocurre en -applicationWillResignActive: y cancele la animación retrasada en -applicationDidEnterBackground:
- (void) applicationWillResignActive:(UIApplication *)application {
// Measure the time between -applicationWillResignActive: and -applicationDidEnterBackground first!
[self performSelector:@selector(pauseAnimation) withObject:nil afterDelay:0.1];
// OTHER CODE HERE
}
- (void) applicationDidEnterBackground:(UIApplication *)application {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pauseAnimation) object:nil];
// OTHER CODE HERE
}
La solución runloop en realidad resulta en algunos problemas con la aplicación.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
Si va al fondo y vuelve a abrir la aplicación inmediatamente, la aplicación se convertirá en una pantalla negra. Cuando vuelve a abrir la aplicación por segunda vez, todo vuelve a la normalidad.
Una mejor manera es usar
[CATransaction flush]
Esto obliga a que todas las transacciones actuales se apliquen de inmediato y no tiene el problema que resulta en una pantalla negra.