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:
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
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
Nota Bene: este ejemplo es la solución de guión gráfico / código y supone que uno ha establecido restricciones predeterminadas en el guión gráfico. Entonces uno puede animar los cambios usando el código.
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):
lazy var centerYInflection:NSLayoutConstraint = {
let temp = self.view.constraints.filter({ $0.firstItem is MNGStarRating }).filter ( { $0.secondItem is UIWebView }).filter({ $0.firstAttribute == .CenterY }).first
return temp!
}()
....
Algún tiempo después...
@IBAction func toggleRatingView (sender:AnyObject){
let aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
self.view.layoutIfNeeded()
//Use any animation you want, I like the bounce in springVelocity...
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.75, options: [.CurveEaseOut], animations: { () -> Void in
//I use the frames to determine if the view is on-screen
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to give the animation some life
self.centerYInflection.constant = aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
} else {
//I play a different sound just to keep the user engaged
//out of frame ~ animate into scene
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
}
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 ejemplo
- Si es necesario, no dude en usar los márgenes de diseño asociados con una vista que participa en la definición de restricción
let viewMargins = self.webview.layoutMarginsGuide
: es un ejemplo
- No haga el trabajo que no tiene que hacer, todas las vistas con restricciones en el guión gráfico tienen restricciones asociadas a la propiedad self.viewName.constraints
- Cambie sus prioridades para cualquier restricción a menos de 1000. Establecí el mío en 250 (bajo) o 750 (alto) en el guión gráfico; (si intenta cambiar una prioridad de 1000 a algo en el código, la aplicación se bloqueará porque se requiere 1000)
- Considere no intentar usar de inmediato activarConstraints y desactivarConstraints (tienen su lugar, pero cuando solo está aprendiendo o si está usando un guión gráfico con estos, probablemente significa que está haciendo demasiado ~ aunque sí tienen un lugar como se ve a continuación)
- Considere no usar addConstraints / removeConstraints a menos que realmente esté agregando una nueva restricción en el código. Descubrí que la mayoría de las veces diseño las vistas en el guión gráfico con las restricciones deseadas (colocando la vista fuera de la pantalla), luego en el código, animo las restricciones creadas previamente en el guión gráfico para mover la vista.
- Pasé mucho tiempo perdido construyendo restricciones con la nueva clase y subclases NSAnchorLayout. Funcionan bien, pero me tomó un tiempo darme cuenta de que todas las limitaciones que necesitaba ya existían en el guión gráfico. Si crea restricciones en el código, entonces seguramente use este método para agregar sus restricciones:
Muestra rápida de soluciones a EVITAR cuando se utilizan guiones gráficos
private var _nc:[NSLayoutConstraint] = []
lazy var newConstraints:[NSLayoutConstraint] = {
if !(self._nc.isEmpty) {
return self._nc
}
let viewMargins = self.webview.layoutMarginsGuide
let minimumScreenWidth = min(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height)
let centerY = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.webview.centerYAnchor)
centerY.constant = -1000.0
centerY.priority = (950)
let centerX = self.ratingView.centerXAnchor.constraintEqualToAnchor(self.webview.centerXAnchor)
centerX.priority = (950)
if let buttonConstraints = self.originalRatingViewConstraints?.filter({
($0.firstItem is UIButton || $0.secondItem is UIButton )
}) {
self._nc.appendContentsOf(buttonConstraints)
}
self._nc.append( centerY)
self._nc.append( centerX)
self._nc.append (self.ratingView.leadingAnchor.constraintEqualToAnchor(viewMargins.leadingAnchor, constant: 10.0))
self._nc.append (self.ratingView.trailingAnchor.constraintEqualToAnchor(viewMargins.trailingAnchor, constant: 10.0))
self._nc.append (self.ratingView.widthAnchor.constraintEqualToConstant((minimumScreenWidth - 20.0)))
self._nc.append (self.ratingView.heightAnchor.constraintEqualToConstant(200.0))
return self._nc
}()
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:
NB: Tómese un momento para leer la sección AutoLayout a continuación y la guía original. Hay una manera de utilizar estas técnicas para complementar sus animadores dinámicos.
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1.0, options: [.CurveEaseOut], animations: { () -> Void in
//
if self.starTopInflectionPoint.constant < 0 {
//-3000
//offscreen
self.starTopInflectionPoint.constant = self.navigationController?.navigationBar.bounds.height ?? 0
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
} else {
self.starTopInflectionPoint.constant = -3000
self.changeConstraintPriority([self.starTopInflectionPoint], value: UILayoutPriority(950), forView: self.ratingView)
}
}) { (success) -> Void in
//do something else
}
}
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í:
[containerView layoutIfNeeded]; // Ensures that all pending layout operations have been completed
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[containerView layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes
}];
En OS X, use el siguiente código cuando use animaciones respaldadas por capas:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
Cuando no esté utilizando animaciones respaldadas por capas, debe animar la constante utilizando el animador de la restricción:
[[constraint animator] setConstant:42];
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.