Storyboard, código, dicas e algumas dicas
As outras respostas são ótimas, mas essa destaca algumas dicas bastante importantes sobre como animar restrições usando um exemplo recente. Passei por muitas variações antes de perceber o seguinte:
Faça as restrições que você deseja direcionar em variáveis de classe para manter uma referência forte. No Swift, usei variáveis preguiçosas:
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!
}()
Após algumas experiências, observei que DEVE obter a restrição a partir da vista ACIMA (também conhecida como superview), as duas visualizações em que a restrição está definida. No exemplo abaixo (MNGStarRating e UIWebView são os dois tipos de itens entre os quais estou criando uma restrição e são subvisões em self.view).
Encadeamento de filtros
Aproveito o método de filtro de Swift para separar a restrição desejada que servirá como ponto de inflexão. Pode-se também ficar muito mais complicado, mas o filtro faz um bom trabalho aqui.
Animando restrições usando o Swift
Nota Bene - Este exemplo é a solução de storyboard / código e assume que uma tenha feito restrições padrão no storyboard. Pode-se, então, animar as alterações usando o código.
Supondo que você crie uma propriedade para filtrar com critérios precisos e chegar a um ponto de inflexão específico para sua animação (é claro que você também pode filtrar uma matriz e fazer um loop, se precisar de várias restrições):
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!
}()
....
Algum tempo depois...
@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
}
}
}
As muitas voltas erradas
Essas notas são realmente um conjunto de dicas que escrevi para mim. Eu fiz tudo o que não pessoalmente e dolorosamente. Espero que este guia possa poupar outros.
Cuidado com o zPositioning. Às vezes, quando aparentemente nada está acontecendo, você deve ocultar algumas das outras visualizações ou usar o depurador de visualizações para localizar sua visualização animada. Até encontrei casos em que um Atributo de Tempo de Execução Definido pelo Usuário foi perdido no xml de um storyboard e levou à cobertura animada da exibição (enquanto trabalhava).
Reserve sempre um minuto para ler a documentação (nova e antiga), a Ajuda rápida e os cabeçalhos. A Apple continua fazendo muitas alterações para gerenciar melhor as restrições do AutoLayout (consulte visualizações de pilha). Ou pelo menos o livro de receitas do AutoLayout . Lembre-se de que às vezes as melhores soluções estão na documentação / nos vídeos mais antigos.
Brinque com os valores na animação e considere usar outras variantes animateWithDuration.
Não codifique valores de layout específicos como critérios para determinar alterações em outras constantes; em vez disso, use valores que permitam determinar o local da exibição. CGRectContainsRect
é um exemplo
- Se necessário, não hesite em usar as margens do layout associadas a uma visualização que participa da definição de restrição
let viewMargins = self.webview.layoutMarginsGuide
: por exemplo
- Não faça o trabalho que não precisa, todas as visualizações com restrições no storyboard têm restrições associadas à propriedade self.viewName.constraints
- Mude suas prioridades para quaisquer restrições para menos de 1000. Defino a minha como 250 (baixa) ou 750 (alta) no storyboard; (se você tentar alterar uma prioridade de 1000 para qualquer coisa no código, o aplicativo falhará porque 1000 é necessário)
- Considere não tentar imediatamente usar o activeConstraints e o deactivateConstraints (eles têm o lugar deles, mas quando estão aprendendo ou se você estiver usando um storyboard usando isso provavelmente significa que você está fazendo muito ~ eles têm um lugar, como visto abaixo)
- Considere não usar addConstraints / removeConstraints, a menos que você esteja realmente adicionando uma nova restrição no código. Descobri que, na maioria das vezes, faço o layout das visualizações no storyboard com as restrições desejadas (colocando a visualização fora da tela) e, em código, animao as restrições criadas anteriormente no storyboard para mover a visualização.
- Passei muito tempo desperdiçado construindo restrições com a nova classe e subclasses NSAnchorLayout. Isso funciona muito bem, mas levei um tempo para perceber que todas as restrições que eu precisava já existiam no storyboard. Se você criar restrições no código, certamente use este método para agregar suas restrições:
Amostra rápida de soluções para EVITAR ao usar Storyboards
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
}()
Se você esquecer uma dessas dicas ou as mais simples, como onde adicionar o layoutIfNeeded, provavelmente nada acontecerá: nesse caso, você pode ter uma solução meia cozida como esta:
NB - Leia a seção AutoLayout abaixo e o guia original. Existe uma maneira de usar essas técnicas para complementar seus 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
}
}
Snippet do Guia de AutoLayout (observe que o segundo snippet é para usar o OS X). BTW - Isso não está mais no guia atual, tanto quanto posso ver. As técnicas preferidas continuam a evoluir.
Animando alterações feitas pelo layout automático
Se você precisar de controle total sobre a animação de alterações feitas pelo Layout Automático, faça suas alterações de restrição programaticamente. O conceito básico é o mesmo para iOS e OS X, mas existem algumas pequenas diferenças.
Em um aplicativo iOS, seu código seria semelhante ao seguinte:
[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
}];
No OS X, use o seguinte código ao usar animações com suporte de camada:
[containterView layoutSubtreeIfNeeded];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setAllowsImplicitAnimation: YES];
// Make all constraint changes here
[containerView layoutSubtreeIfNeeded];
}];
Quando você não está usando animações com suporte de camada, é necessário animar a constante usando o animador da restrição:
[[constraint animator] setConstant:42];
Para quem aprende melhor visualmente, confira este vídeo da Apple .
Preste muita atenção
Muitas vezes, na documentação, existem pequenas notas ou partes de código que levam a idéias maiores. Por exemplo, anexar restrições de layout automático a animadores dinâmicos é uma grande ideia.
Boa sorte e que a força esteja com você.