Estoy actualizando una aplicación antigua con un AdBannerView
y cuando no hay ningún anuncio, se desliza fuera de la pantalla. Cuando hay un anuncio, se desliza en la pantalla. Cosas básicas.
Estilo antiguo, puse el marco en un bloque de animación. Nuevo estilo, tengo una IBOutlet
restricción de diseño automático que determina la Y
posición, en este caso, es la distancia desde la parte inferior de la supervista y modifica la constante:
- (void)moveBannerOffScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = -32;
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = 0;
}];
bannerIsVisible = TRUE;
}
Y el banner se mueve, exactamente como se esperaba, pero no hay animación.
ACTUALIZACIÓN: volví a ver la charla de WWDC 12 Mejores prácticas para dominar el diseño automático que cubre la animación. Discute cómo actualizar restricciones usando CoreAnimation :
He intentado con el siguiente código, pero obtengo exactamente los mismos resultados:
- (void)moveBannerOffScreen {
_addBannerDistanceFromBottomConstraint.constant = -32;
[UIView animateWithDuration:2 animations:^{
[self.view setNeedsLayout];
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
_addBannerDistanceFromBottomConstraint.constant = 0;
[UIView animateWithDuration:2 animations:^{
[self.view setNeedsLayout];
}];
bannerIsVisible = TRUE;
}
En una nota al margen, lo he comprobado varias veces y esto se está ejecutando en el hilo principal .
Respuestas:
Dos notas importantes:
Debe llamar
layoutIfNeeded
dentro del bloque de animación. Apple realmente recomienda que lo llame una vez antes del bloque de animación para asegurarse de que se hayan completado todas las operaciones de diseño pendientesDebe llamarlo específicamente en la vista principal (p
self.view
. Ej. ), No en la vista secundaria que tiene las restricciones asociadas. Al hacerlo, se actualizarán todas las vistas restringidas, incluida la animación de otras vistas que podrían estar restringidas a la vista de la que cambió la restricción (por ejemplo, la Vista B se adjunta a la parte inferior de la Vista A y acaba de cambiar el desplazamiento superior de la Vista A y desea la Vista B animar con eso)Prueba esto:
C objetivo
Swift 3
fuente
setNeedsLayout
lugar delayoutIfNeeded
. Estoy un poco horrorizado por la cantidad de horas que pasé sin darme cuenta de que solo escribí el nombre del método incorrecto.Agradezco la respuesta proporcionada, pero creo que sería bueno llevarla un poco más lejos.
La animación básica de bloques de la documentación.
pero realmente este es un escenario muy simplista. ¿Qué sucede si quiero animar restricciones de subvista a través del
updateConstraints
método?Un bloque de animación que llama al método updateConstraints de subvistas
El método updateConstraints se anula en la subclase UIView y debe llamar a super al final del método.
La guía de diseño automático deja mucho que desear, pero vale la pena leerla. Yo mismo estoy usando esto como parte de una
UISwitch
que alterna una subvista con un par deUITextField
s con una animación de colapso simple y sutil (0.2 segundos de duración). Las restricciones para la subvista se manejan en los métodos updateConstraints de las subclases de UIView como se describió anteriormente.fuente
self.view
(y no en una subvista de esa vista),updateConstraintsIfNeeded
no es necesario llamar (porquesetNeedsLayout
también desencadenaupdateConstraints
esa vista). Podría ser trivial para la mayoría, pero no fue para mí hasta ahora;)En general, solo necesita actualizar las restricciones y llamar
layoutIfNeeded
dentro del bloque de animación. Esto puede ser cambiar la.constant
propiedad de unNSLayoutConstraint
, agregar restricciones de eliminación (iOS 7) o cambiar la.active
propiedad de las restricciones (iOS 8 y 9).Código de muestra:
Configuración de muestra:
Controversia
Hay algunas preguntas sobre si la restricción debe cambiarse antes del bloque de animación o dentro de él (ver respuestas anteriores).
La siguiente es una conversación en Twitter entre Martin Pilkington, que enseña iOS, y Ken Ferry, quien escribió Auto Layout. Ken explica que aunque cambiar constantes fuera del bloque de animación puede funcionar actualmente , no es seguro y realmente deberían cambiarse dentro del bloque de animación. https://twitter.com/kongtomorrow/status/440627401018466305
Animación:
Proyecto de muestra
Aquí hay un proyecto simple que muestra cómo se puede animar una vista. Está utilizando el Objetivo C y anima la vista cambiando la
.active
propiedad de varias restricciones. https://github.com/shepting/SampleAutoLayoutAnimationfuente
fuente
Solución Swift 4
UIView.animate
Tres simples pasos:
Cambiar las restricciones, por ejemplo:
Indique al contenedor
view
que su diseño está sucio y que la distribución automática debe volver a calcular el diseño:En el bloque de animación, dígale al diseño que vuelva a calcular el diseño, que es equivalente a configurar los cuadros directamente (en este caso, la distribución automática establecerá los cuadros):
Completo ejemplo más simple:
Nota al margen
Hay un paso 0 opcional: antes de cambiar las restricciones, es posible que desee llamar
self.view.layoutIfNeeded()
para asegurarse de que el punto de partida para la animación sea del estado con las restricciones anteriores aplicadas (en caso de que hubiera otros cambios de restricciones que no deberían incluirse en la animación ):UIViewPropertyAnimator
Dado que con iOS 10 tenemos un nuevo mecanismo de animación
UIViewPropertyAnimator
, deberíamos saber que básicamente se aplica el mismo mecanismo. Los pasos son básicamente los mismos:Dado que
animator
es una encapsulación de la animación, podemos mantener referencia a ella y llamarla más tarde. Sin embargo, dado que en el bloque de animación simplemente le decimos al autolayout que recalcule los cuadros, tenemos que cambiar las restricciones antes de llamarstartAnimation
. Por lo tanto, algo como esto es posible:El orden de cambiar las restricciones y comenzar un animador es importante: si solo cambiamos las restricciones y dejamos nuestro animador para algún punto posterior, el siguiente ciclo de redibujo puede invocar el recálculo de la distribución automática y el cambio no se animará.
Además, recuerde que un solo animador no es reutilizable: una vez que lo ejecuta, no puede "volver a ejecutarlo". Así que supongo que no hay una buena razón para mantener el animador, a menos que lo usemos para controlar una animación interactiva.
fuente
Storyboard, Código, Consejos y algunas Gotchas
Las otras respuestas están bien, pero esta resalta algunas trampas bastante importantes de restricciones de animación usando un ejemplo reciente. Pasé por muchas variaciones antes de darme cuenta de lo siguiente:
Convierta las restricciones que desea apuntar en variables de clase para mantener una referencia fuerte. En Swift usé variables perezosas:
Después de experimentar un poco, noté que DEBE obtener la restricción de la vista ARRIBA (también conocida como la supervista) las dos vistas donde se define la restricción. En el ejemplo a continuación (tanto MNGStarRating como UIWebView son los dos tipos de elementos entre los que estoy creando una restricción, y son subvistas dentro de self.view).
Encadenamiento de filtro
Aprovecho el método de filtro de Swift para separar la restricción deseada que servirá como punto de inflexión. También podría ser mucho más complicado, pero el filtro hace un buen trabajo aquí.
Animar restricciones usando Swift
Suponiendo que cree una propiedad para filtrar con criterios precisos y llegue a un punto de inflexión específico para su animación (por supuesto, también puede filtrar una matriz y recorrerla si necesita múltiples restricciones):
....
Algún tiempo después...
Los muchos giros equivocados
Estas notas son realmente un conjunto de consejos que escribí para mí. Hice todo lo que no debo hacer personal y dolorosamente. Esperemos que esta guía pueda ahorrarle a otros.
Tenga cuidado con zPosposition. A veces, cuando aparentemente no ocurre nada, debe ocultar algunas de las otras vistas o usar el depurador de vistas para ubicar su vista animada. Incluso he encontrado casos en los que un Atributo de tiempo de ejecución definido por el usuario se perdió en el xml de un guión gráfico y condujo a que se cubriera la vista animada (mientras trabajaba).
Siempre tome un minuto para leer la documentación (nueva y antigua), la Ayuda rápida y los encabezados. Apple sigue haciendo muchos cambios para gestionar mejor las restricciones de AutoLayout (ver vistas de pila). O al menos el AutoLayout Cookbook . Tenga en cuenta que a veces las mejores soluciones se encuentran en la documentación / videos anteriores.
Juega con los valores de la animación y considera usar otras variantes de animateWithDuration.
No codifique valores de diseño específicos como criterios para determinar cambios en otras constantes, en su lugar use valores que le permitan determinar la ubicación de la vista.
CGRectContainsRect
es un ejemplolet viewMargins = self.webview.layoutMarginsGuide
: es un ejemploMuestra rápida de soluciones a EVITAR cuando se utilizan guiones gráficos
Si olvida uno de estos consejos o los más simples, como dónde agregar el diseño Si es necesario, lo más probable es que no suceda nada: en cuyo caso, puede tener una solución medio cocida como esta:
Fragmento de la Guía de diseño automático (tenga en cuenta que el segundo fragmento es para usar OS X). Por cierto, esto ya no está en la guía actual por lo que puedo ver. Las técnicas preferidas continúan evolucionando.
Animación de cambios realizados por diseño automático
Si necesita un control total sobre los cambios de animación realizados por Auto Layout, debe hacer que sus restricciones cambien mediante programación. El concepto básico es el mismo tanto para iOS como para OS X, pero hay algunas diferencias menores.
En una aplicación de iOS, su código se vería así:
En OS X, use el siguiente código cuando use animaciones respaldadas por capas:
Cuando no esté utilizando animaciones respaldadas por capas, debe animar la constante utilizando el animador de la restricción:
Para aquellos que aprenden mejor visualmente, vean este video temprano de Apple .
Presta mucha atención
A menudo, en la documentación hay pequeñas notas o fragmentos de código que conducen a ideas más grandes. Por ejemplo, adjuntar restricciones de diseño automático a animadores dinámicos es una gran idea.
Buena suerte y que la fuerza te acompañe.fuente
Solución rápida:
fuente
Solución de trabajo 100% Swift 3.1
He leído todas las respuestas y quiero compartir el código y la jerarquía de líneas que he usado en todas mis aplicaciones para animarlas correctamente. Algunas soluciones aquí no funcionan, debe verificarlas en dispositivos más lentos, por ejemplo, iPhone 5 en este momento.
fuente
Estaba tratando de animar restricciones y no fue realmente fácil encontrar una buena explicación.
Lo que dicen otras respuestas es totalmente cierto: debe llamar al
[self.view layoutIfNeeded];
interioranimateWithDuration: animations:
. Sin embargo, el otro punto importante es tener punteros para todo loNSLayoutConstraint
que quieras animar.Creé un ejemplo en GitHub .
fuente
Solución funcional y recién probada para Swift 3 con Xcode 8.3.3:
Solo tenga en cuenta que self.calendarViewHeight es una restricción referida a customView (CalendarView). Llamé a .layoutIfNeeded () en self.view y NO en self.calendarView
Espero que esto ayude.
fuente
Hay un artículo sobre esto: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was
En el cual, codificó así:
Espero eso ayude.
fuente
En el contexto de la animación de restricciones, me gustaría mencionar una situación específica en la que animé una restricción inmediatamente dentro de una notificación de teclado abierto.
La restricción definió un espacio superior desde un campo de texto hasta la parte superior del contenedor. Al abrir el teclado, divido la constante por 2.
No pude lograr una animación de restricción uniforme consistente directamente dentro de la notificación del teclado. Aproximadamente la mitad de las veces la vista simplemente saltaría a su nueva posición, sin animación.
Se me ocurrió que podría haber algún diseño adicional como resultado de la apertura del teclado. Agregar un simple bloque dispatch_after con un retraso de 10 ms hizo que la animación se ejecutara siempre, sin saltar.
fuente