Como animar alterações de restrição?

959

Estou atualizando um aplicativo antigo com um AdBannerViewe, 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 IBOutletrestrição de layout automático que determina a Yposiçã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 .

DBD
fonte
11
Eu nunca vi tantos votos oferecidos para uma pergunta e resposta em um erro de digitação no SO antes
abbood
3
Se houver um erro de digitação na resposta, edite a resposta. É por isso que eles são editáveis.
i_am_jorf
@jeffamaphone - Seria mais útil se você apontasse o erro de digitação, para que eu soubesse onde estava o erro. Você mesmo pode editar a resposta e consertar o erro de digitação, salvando todos os outros da nossa diatribe. Acabei de editá-lo para remover a constante do bloco de animação, se é o que você estava se referindo.
DBD 07/10
1
Não sei qual é o erro de digitação. Eu estava respondendo aos comentários acima.
I_am_jorf 07/10
9
Então o erro de digitação é a questão. Estupidamente, eu estava digitando "setNeedsLayout" em vez de "layoutIfNeeded". Isso é mostrado claramente na minha pergunta quando recordo e colo meu código com o erro e as capturas de tela com o comando correto. No entanto, não conseguia perceber até alguém apontar.
DBD 07/10

Respostas:

1697

Duas notas importantes:

  1. Você precisa ligar layoutIfNeededdentro 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ídas

  2. Você 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

- (void)moveBannerOffScreen {
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = -32;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = FALSE;
}

- (void)moveBannerOnScreen { 
    [self.view layoutIfNeeded];

    [UIView animateWithDuration:5
        animations:^{
            self._addBannerDistanceFromBottomConstraint.constant = 0;
            [self.view layoutIfNeeded]; // Called on parent view
        }];
    bannerIsVisible = TRUE;
}

Swift 3

UIView.animate(withDuration: 5) {
    self._addBannerDistanceFromBottomConstraint.constant = 0
    self.view.layoutIfNeeded()
}
g3rv4
fonte
199
Você sabe o que ... sua resposta funciona. A WWDC funciona .... minha visão falha. Por alguma razão, levei uma semana para perceber que eu estava ligando em setNeedsLayoutvez de layoutIfNeeded. Estou um pouco horrorizado com quantas horas passei sem perceber, apenas digitei o nome do método errado.
DBD
23
A solução funciona, mas você não precisa alterar a constante de restrição no bloco de animação. É muito bom definir a restrição uma vez antes de iniciar a animação. Você deve editar sua resposta.
Ortwin Gentz ​​14/01
74
Inicialmente, isso não funcionou para mim e, então, percebi que era necessário chamar layoutIfNeeded na visualização PARENT, não na visualização à qual as restrições se aplicam.
Oliver Pearmain 21/03
20
usar layoutIfNeeded animará todas as atualizações da subvisão e não apenas a alteração de restrição. como você anima apenas a alteração de restrição?
N24 24/08
11
"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ídas", obrigado, nunca pensei nisso, mas faz sentido.
Rick van der Linde
109

Agradeço a resposta, mas acho que seria bom ir um pouco mais longe.

A animação básica do bloco da documentação

[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
}];

mas realmente este é um cenário muito simplista. E se eu quiser animar restrições de subvisão através do updateConstraintsmétodo?

Um bloco de animação que chama o subviews updateConstraints método

[self.view layoutIfNeeded];
[self.subView setNeedsUpdateConstraints];
[self.subView updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
    [self.view layoutIfNeeded];
} completion:nil];

O método updateConstraints é substituído na subclasse UIView e deve chamar super no final do método.

- (void)updateConstraints
{
    // Update some constraints

    [super updateConstraints];
}

O Guia de AutoLayout deixa muito a desejar, mas vale a pena ler. Eu mesmo estou usando isso como parte de um UISwitchque alterna uma subvisão com um par de UITextFields 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.

Cameron Lowell Palmer
fonte
Ao chamar todos os métodos acima em self.view(e não em uma subvisão dessa visão), a chamada updateConstraintsIfNeedednão é necessária (porque setNeedsLayouttambém aciona updateConstraintsessa visão). Pode ser trivial para a maioria, mas não era para mim até agora;)
anneblue 24/02
É difícil comentar sem conhecer a interação completa do layout de layout automático e de molas e suportes no seu caso particular. Estou falando inteiramente de um cenário de autolayout puro. Gostaria de saber o que exatamente está acontecendo no seu método layoutSubviews.
Cameron Lowell Palmer
translatesAutoresizingMaskIntoConstraints = NO; Se você deseja fazer o autolayout puro, desabilite-o definitivamente.
Cameron Lowell Palmer
Eu não vi isso, mas não posso realmente falar sobre o problema sem algum código. Talvez você possa simular um TableView e colá-lo no GitHub?
Cameron Lowell Palmer
Desculpe, eu não uso gitHub :(
anneblue 26/02
74

Geralmente, você só precisa atualizar as restrições e chamar layoutIfNeededdentro do bloco de animação. Isso pode alterar a .constantpropriedade de um NSLayoutConstraint, adicionar restrições de remoção (iOS 7) ou alterar a .activepropriedade de restrições (iOS 8 e 9).

Código de amostra:

[UIView animateWithDuration:0.3 animations:^{
    // Move to right
    self.leadingConstraint.active = false;
    self.trailingConstraint.active = true;

    // Move to bottom
    self.topConstraint.active = false;
    self.bottomConstraint.active = true;

    // Make the animation happen
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

Exemplo de configuração:

Projeto Xcode, para experimentar um projeto de animaçã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 .activepropriedade de várias restrições. https://github.com/shepting/SampleAutoLayoutAnimation

Steven Hepting
fonte
+1 para mostrar um exemplo usando o novo sinalizador ativo. Além disso, alterar a restrição de fora do bloco de animação sempre me senti como um hack para mim
Korey Hinton
Eu levantei um problema no github porque essa solução não funciona para mover a exibição de volta para onde estava. Acredito que mudar ativo não é a solução correta e você deve mudar a prioridade. Defina a parte superior como 750 e a parte inferior como 250; no código, alterne entre UILayoutPriorityDefaultHigh e UILayoutPriorityDefaultLow.
malhal
Outro motivo para atualizar dentro do bloco é que ele isola as alterações do código de chamada que também podem chamar layoutIfNeeded. (e obrigado pelo link do Twitter)
Chris Conover
1
Ótima resposta. Especialmente porque você mencionou a conversa de Martin e Ken sobre isso.
Jona
Estou confuso sobre a maneira de atualizar restrições e escrevi isso . Você pode dar uma olhada?
Mel
36
// Step 1, update your constraint
self.myOutletToConstraint.constant = 50; // New height (for example)

// Step 2, trigger animation
[UIView animateWithDuration:2.0 animations:^{

    // Step 3, call layoutIfNeeded on your animated view's parent
    [self.view layoutIfNeeded];
}];
John Erck
fonte
29

Solução Swift 4

UIView.animate

Três etapas simples:

  1. Altere as restrições, por exemplo:

    heightAnchor.constant = 50
  2. Diga ao conteúdo viewque o layout está sujo e que o layout automático deve recalcular o layout:

    self.view.setNeedsLayout()
  3. 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):

    UIView.animate(withDuration: 0.5) {
        self.view.layoutIfNeeded()
    }

Exemplo mais simples completo:

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

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) ):

otherConstraint.constant = 30
// this will make sure that otherConstraint won't be animated but will take effect immediately
self.view.layoutIfNeeded()

heightAnchor.constant = 50
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.5) {
    self.view.layoutIfNeeded()
}

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:

heightAnchor.constant = 50
self.view.setNeedsLayout()
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}
animator.startAnimation()

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 chamar startAnimation. Portanto, algo como isso é possível:

// prepare the animator first and keep a reference to it
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
animator.addAnimations {
    self.view.layoutIfNeeded()
}

// at some other point in time we change the constraints and call the animator
heightAnchor.constant = 50
self.view.setNeedsLayout()
animator.startAnimation()

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.

Milan Nosáľ
fonte
Também funciona bem no swift 5
spnkr 7/03
layoutIfNeeded () é a chave
Medhi
15

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.

  1. 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).

  2. 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.

  3. Brinque com os valores na animação e considere usar outras variantes animateWithDuration.

  4. 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

  5. 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
  6. 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
  7. 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)
  8. 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)
  9. 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.
  10. 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ê.

Tommie C.
fonte
11

Solução rápida:

yourConstraint.constant = 50
UIView.animate(withDuration: 1.0, animations: {
    yourView.layoutIfNeeded
})
Nazariy Vlizlo
fonte
7

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.

self.view.layoutIfNeeded() // Force lays of all subviews on root view
UIView.animate(withDuration: 0.5) { [weak self] in // allowing to ARC to deallocate it properly
       self?.tbConstraint.constant = 158 // my constraint constant change
       self?.view.layoutIfNeeded() // Force lays of all subviews on root view again.
}
C0mrade
fonte
4

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];dentro animateWithDuration: animations:. No entanto, o outro ponto importante é ter ponteiros para todos os que NSLayoutConstraintvocê deseja animar.

Eu criei um exemplo no GitHub .

Gabriel.Massana
fonte
4

Solução funcional e testada para o Swift 3 com o Xcode 8.3.3:

self.view.layoutIfNeeded()
self.calendarViewHeight.constant = 56.0

UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.view.layoutIfNeeded()
    }, completion: nil)

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.

Edoardo Vicoli
fonte
3

Há um artigo sobre isso: http://weblog.invasivecode.com/post/42362079291/auto-layout-and-core-animation-auto-layout-was

Em que, ele codificou assim:

- (void)handleTapFrom:(UIGestureRecognizer *)gesture {
    if (_isVisible) {
        _isVisible = NO;
        self.topConstraint.constant = -44.;    // 1
        [self.navbar setNeedsUpdateConstraints];  // 2
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded]; // 3
        }];
    } else {
        _isVisible = YES;
        self.topConstraint.constant = 0.;
        [self.navbar setNeedsUpdateConstraints];
        [UIView animateWithDuration:.3 animations:^{
            [self.navbar layoutIfNeeded];
        }];
    }
}

Espero que ajude.

DD
fonte
1

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.

ivanferdelja
fonte