web-dev-qa-db-fra.com

NSLayoutConstraints est-il animable?

J'essaie d'animer certaines vues pour qu'elles soient bloquées par le clavier géant en mode paysage. Cela fonctionne bien si j'anime simplement les cadres, mais d'autres ont suggéré que c'était contre-productif et que je devrais plutôt mettre à jour NSLayoutConstraints. Cependant, ils ne semblent pas pouvoir être animés. Est-ce que quelqu'un les a obligés à travailler avec succès?

//heightFromTop is an NSLayoutConstraint referenced from IB
[UIView animateWithDuration:0.25 animations:^{
    self.heightFromTop.constant= 550.f;
}];

Le résultat est un saut instantané à la hauteur en question.

194
borrrden

Il suffit de suivre ce modèle exact:

self.heightFromTop.constant = 550.0f;
[myView setNeedsUpdateConstraints];

[UIView animateWithDuration:0.25f animations:^{
   [myView layoutIfNeeded];
}];

myView est la vue où self.heightFromTop a été ajouté à. Votre vue est "en train de" sauter car la seule chose que vous avez faite dans le bloc d'animation a été de définir la contrainte, ce qui ne provoque pas immédiatement de dispositions. Dans votre code, la présentation se produit lors de la prochaine boucle d'exécution après avoir défini heightFromTop.constant et vous êtes déjà hors de portée du bloc d’animation.

Dans Swift 2:

self.heightFromTop.constant = 550
myView.setNeedsUpdateConstraints()

UIView.animateWithDuration(0.25, animations: {
   myView.layoutIfNeeded()
})
470
John Estropia

La méthode suggérée par Apple est légèrement différente ( Voir l'exemple dans la section "Animation des modifications apportées par la disposition automatique" ). Vous devez d'abord appeler layoutIfNeeded avant l'animation. Ajoutez ensuite vos éléments d'animation à l'intérieur du bloc d'animation, puis appelez à nouveau layoutIfNeeded à la fin. Pour les gars comme moi qui sont en train de passer à l'autolayout, c'est plus similaire aux animations précédentes que nous faisions avec les images à l'intérieur de blocs d'animation. Nous avons juste besoin d'appeler layoutIfNeeded deux fois - avant les animations et après les animations:

[self.view layoutIfNeeded]; // Ensures that all pending layout operations have been completed

[UIView animateWithDuration:1.0f animations:^{

  // Make all constraint changes here
  self.heightFromTop.constant= 550.f;

  [self.view layoutIfNeeded]; // Forces the layout of the subtree animation block and then captures all of the frame changes

}];
80
Centurion

J'ai essayé l'approche de @ Centurion, mais ma vue s'animerait sur une mauvaise image si elle est chargée à partir du storyboard. Le problème disparaît si je remplace le premier layoutIfNeeded par updateConstraintsIfNeeded, bien que je ne sache pas pourquoi. Si quelqu'un peut donner une explication, ce serait très apprécié.

[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
    self.myConstraint.constant= 100;
    [self.view layoutIfNeeded];
}];
10
Joseph Lin

J'avais un problème similaire et ce fil était d'une grande aide pour le surmonter.

La réponse de erurainon m'a mis sur la bonne voie, mais j'aimerais proposer une réponse légèrement différente. Le code proposé par erurainon ne fonctionnait pas pour moi, car j'avais toujours un saut au lieu d'une transition animée. Le lien fourni par cnotethegr8 m'a donné la réponse de travail:

Guide de mise en page automatique https://developer.Apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample .html (jusqu'en bas de la page).

Quelques différences avec la réponse de erurainon:

  1. Appelez layoutIfNeeded sur la vue du conteneur avant l'appel d'une méthode d'animation (et au lieu de setNeedsUpdateConstraints on myView).
  2. Définissez la nouvelle contrainte dans le bloc d'animations.
  3. Appelez layoutIfNeeded dans la vue conteneur dans la méthode des animations (après avoir défini la contrainte), plutôt que dans myView.

Cela sera conforme au modèle suggéré par Apple dans le lien ci-dessus.

Un exemple

Je voulais animer une vue particulière en la fermant ou en l'agrandissant d'un simple clic. Étant donné que j'utilise autolayout et que je ne souhaite coder aucune dimension (dans le cas de ma hauteur) dans le code, j'ai décidé de capturer la hauteur dans viewDidLayoutSubviews. Vous devez utiliser cette méthode et non viewWillAppear lorsque vous utilisez autolayout. Comme viewDidLayoutSubviews peut être appelé plusieurs fois, j'ai utilisé un BOOL pour me tenir au courant de la première exécution de mon initialisation.

// Code snippets

@property (weak, nonatomic) IBOutlet UIView *topView; // Container for minimalView
@property (weak, nonatomic) IBOutlet UIView *minimalView; // View to animate

@property (nonatomic) CGFloat minimalViewFullHeight; // Original height of minimalView

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *minimalViewHeightConstraint;

@property (nonatomic) BOOL executedViewDidLayoutSubviews;


- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];


    // First execution of viewDidLayoutSubviews?
    if(!self.executedViewDidLayoutSubviews){
        self.executedViewDidLayoutSubviews = YES;


        // Record some original dimensions
        self.minimalViewFullHeight = self.minimalView.bounds.size.height;


        // Setup our initial view configuration & let system know that 
        // constraints need to be updated.
        self.minimalViewHeightConstraint.constant = 0.0;
        [self.minimalView setNeedsUpdateConstraints];

        [self.topView layoutIfNeeded];
    }
}

Redimensionner l'extrait d'action complet

// An action to close our minimal view and show our normal (full) view
- (IBAction)resizeFullAction:(UIButton *)sender {

    [self.topView layoutIfNeeded];

    [UIView transitionWithView:self.minimalView
                  duration:1.0
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    self.minimalViewHeightConstraint.constant = 0.0;

                    // Following call to setNeedsUpdateConstraints may not be necessary
                    [self.minimalView setNeedsUpdateConstraints];

                    [self.topView layoutIfNeeded];

                } completion:^(BOOL finished) {
                    ;
                }];

    // Other code to show full view
    // ...
}

Redimensionner le fragment d'action

// An action to open our minimal view and hide our normal (full) view
- (IBAction)resizeSmallAction:(UIButton *)sender {

    [self.topView layoutIfNeeded];

    [UIView transitionWithView:self.minimalView
                  duration:1.0
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                    self.minimalViewHeightConstraint.constant = self.minimalViewFullHeight;
                    [self.minimalView setNeedsUpdateConstraints];

                    [self.topView layoutIfNeeded];

                } completion:^(BOOL finished) {
                    ;
                }];

        // Other code to hide full view
        // ...
    }

Vous pouvez utiliser animateWithDuration au lieu de transitionWithView si vous le souhaitez.

J'espère que cela t'aides.

4
Scott Carter