Estou atualizando um aplicativo antigo com um AdBannerView
e, quando não há anúncio, ele desliza para fora da tela. Quando há um anúncio, ele desliza na tela. Coisas básicas.
Estilo antigo, defino o quadro em um bloco de animação. Novo estilo, tenho uma IBOutlet
restrição de layout automático que determina a Y
posição, neste caso, é a distância da parte inferior da superview e modifico a constante:
- (void)moveBannerOffScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = -32;
}];
bannerIsVisible = FALSE;
}
- (void)moveBannerOnScreen {
[UIView animateWithDuration:5 animations:^{
_addBannerDistanceFromBottomConstraint.constant = 0;
}];
bannerIsVisible = TRUE;
}
E o banner se move exatamente como o esperado, mas sem animação.
ATUALIZAÇÃO: Eu assisti novamente a WWDC 12 falar sobre as práticas recomendadas para dominar o layout automático, que abrange animação. Ele discute como atualizar restrições usando o CoreAnimation :
Eu tentei com o código a seguir, mas obtenha exatamente os mesmos 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;
}
Em uma nota lateral, verifiquei várias vezes e isso está sendo executado no thread principal .
Respostas:
Duas notas importantes:
Você precisa ligar
layoutIfNeeded
dentro do bloco de animação. A Apple realmente recomenda que você o chame uma vez antes do bloco de animação para garantir que todas as operações de layout pendentes tenham sido concluídasVocê precisa chamá-lo especificamente na visão pai (por exemplo
self.view
), não na visão filho que tem as restrições associadas. Fazer isso atualizará todas as visualizações restritas, incluindo a animação de outras visualizações que possam estar restritas à visualização da qual você alterou a restrição (por exemplo, a Visualização B está anexada à parte inferior da Visualização A e você acabou de alterar o deslocamento superior da Visualização A e deseja a Visualização B para animar com ele)Tente o seguinte:
Objetivo-C
Swift 3
fonte
setNeedsLayout
vez delayoutIfNeeded
. Estou um pouco horrorizado com quantas horas passei sem perceber, apenas digitei o nome do método errado.Agradeço a resposta, mas acho que seria bom ir um pouco mais longe.
A animação básica do bloco da documentação
mas realmente este é um cenário muito simplista. E se eu quiser animar restrições de subvisão através do
updateConstraints
método?Um bloco de animação que chama o subviews updateConstraints método
O método updateConstraints é substituído na subclasse UIView e deve chamar super no final do método.
O Guia de AutoLayout deixa muito a desejar, mas vale a pena ler. Eu mesmo estou usando isso como parte de um
UISwitch
que alterna uma subvisão com um par deUITextField
s com uma animação de colapso simples e sutil (0,2 segundos de duração). As restrições para a subvisão estão sendo tratadas nos métodos updateConstraints das subclasses UIView, conforme descrito acima.fonte
self.view
(e não em uma subvisão dessa visão), a chamadaupdateConstraintsIfNeeded
não é necessária (porquesetNeedsLayout
também acionaupdateConstraints
essa visão). Pode ser trivial para a maioria, mas não era para mim até agora;)Geralmente, você só precisa atualizar as restrições e chamar
layoutIfNeeded
dentro do bloco de animação. Isso pode alterar a.constant
propriedade de umNSLayoutConstraint
, adicionar restrições de remoção (iOS 7) ou alterar a.active
propriedade de restrições (iOS 8 e 9).Código de amostra:
Exemplo de configuração:
Controvérsia
Há algumas perguntas sobre se a restrição deve ser alterada antes do bloco de animação ou dentro dele (consulte as respostas anteriores).
A seguir, é apresentada uma conversa no Twitter entre Martin Pilkington, que ensina iOS, e Ken Ferry, que escreveu o Auto Layout. Ken explica que, embora a alteração de constantes fora do bloco de animação possa funcionar atualmente , ela não é segura e elas realmente devem ser alteradas dentro do bloco de animação. https://twitter.com/kongtomorrow/status/440627401018466305
Animação:
Projeto de amostra
Aqui está um projeto simples que mostra como uma exibição pode ser animada. Ele está usando o Objetivo C e anima a exibição alterando a
.active
propriedade de várias restrições. https://github.com/shepting/SampleAutoLayoutAnimationfonte
fonte
Solução Swift 4
UIView.animate
Três etapas simples:
Altere as restrições, por exemplo:
Diga ao conteúdo
view
que o layout está sujo e que o layout automático deve recalcular o layout:No bloco de animação, diga ao layout para recalcular o layout, o que equivale a definir os quadros diretamente (neste caso, o layout automático definirá os quadros):
Exemplo mais simples completo:
Nota
Há uma 0a etapa opcional - antes de alterar as restrições que você deseja chamar
self.view.layoutIfNeeded()
para garantir que o ponto de partida da animação seja do estado com restrições antigas aplicadas (caso existam outras alterações de restrições que não devem ser incluídas na animação) ):UIViewPropertyAnimator
Como no iOS 10, temos um novo mecanismo de animação -
UIViewPropertyAnimator
, devemos saber que basicamente o mesmo mecanismo se aplica a ele. Os passos são basicamente os mesmos:Como
animator
é um encapsulamento da animação, podemos manter a referência e chamá-la mais tarde. No entanto, como no bloco de animação dizemos apenas ao autolayout para recalcular os quadros, precisamos alterar as restrições antes de chamarstartAnimation
. Portanto, algo como isso é possível:A ordem de alterar as restrições e iniciar um animador é importante - se apenas alterarmos as restrições e deixarmos o animador para algum momento posterior, o próximo ciclo de redesenho poderá chamar o recálculo do auto-pagamento e a alteração não será animada.
Além disso, lembre-se de que um único animador não é reutilizável - depois de executá-lo, você não pode "executá-lo novamente". Acho que não há realmente uma boa razão para manter o animador por perto, a menos que o utilizemos para controlar uma animação interativa.
fonte
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:
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
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):
....
Algum tempo depois...
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 exemplolet viewMargins = self.webview.layoutMarginsGuide
: por exemploAmostra rápida de soluções para EVITAR ao usar Storyboards
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:
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:
No OS X, use o seguinte código ao usar animações com suporte de camada:
Quando você não está usando animações com suporte de camada, é necessário animar a constante usando o animador da restrição:
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ê.fonte
Solução rápida:
fonte
Solução de Trabalho 100% Swift 3.1
Eu li todas as respostas e quero compartilhar o código e a hierarquia de linhas que usei em todos os meus aplicativos para animá-las corretamente. Algumas soluções aqui não estão funcionando, você deve verificá-las em dispositivos mais lentos, como o iPhone 5, neste momento.
fonte
Eu estava tentando animar restrições e não foi realmente fácil encontrar uma boa explicação.
O que outras respostas estão dizendo é totalmente verdadeiro: você precisa ligar para
[self.view layoutIfNeeded];
dentroanimateWithDuration: animations:
. No entanto, o outro ponto importante é ter ponteiros para todos os queNSLayoutConstraint
você deseja animar.Eu criei um exemplo no GitHub .
fonte
Solução funcional e testada para o Swift 3 com o Xcode 8.3.3:
Lembre-se de que self.calendarViewHeight é uma restrição referida a um customView (CalendarView). Eu chamei o .layoutIfNeeded () em self.view e NOT em self.calendarView
Espero que esta ajuda.
fonte
Há um artigo sobre isso: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was
Em que, ele codificou assim:
Espero que ajude.
fonte
No contexto da animação de restrições, gostaria de mencionar uma situação específica em que animei uma restrição imediatamente dentro de uma notificação aberta por teclado.
A restrição definiu um espaço superior de um campo de texto para a parte superior do contêiner. Após a abertura do teclado, apenas divido a constante por 2.
Não consegui obter uma animação de restrição suave e consistente diretamente na notificação do teclado. Cerca da metade das vezes a exibição saltaria para sua nova posição - sem animar.
Ocorreu-me que poderia haver algum layout adicional acontecendo como resultado da abertura do teclado. Adicionar um bloco simples dispatch_after com um atraso de 10 ms fazia com que a animação fosse executada toda vez - sem pular.
fonte