NSLayoutConstraints是否可以动画?

时间:2012-10-17 03:14:41

标签: ios animation autolayout

我正在尝试动画一些视图,以便它们被景观中的巨型键盘阻挡。如果我简单地为框架设置动画,它的效果很好,但是其他人认为这会适得其反,我应该更新NSLayoutConstraints。但是,它们似乎不具有动画效果。有没有人让他们成功地工作?

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

结果是立即跳到有问题的高度。

4 个答案:

答案 0 :(得分:467)

请遵循以下确切模式:

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

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

其中myView是添加self.heightFromTop的视图。你的观点是"跳跃"因为你在动画块中唯一做的就是设置约束,这不会立即引起布局。在您的代码中,布局在您设置heightFromTop.constant之后在下一个运行循环中发生,到那时您已经超出了动画块的范围。

在Swift 2中:

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

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

答案 1 :(得分:79)

Apple建议的方式略有不同(See example in "Animating Changes Made by Auto Layout" section)。首先,您需要在动画之前调用layoutIfNeeded。然后在动画块中添加动画内容,然后再次调用layoutIfNeeded。对于像我这样过渡到自动布局的人来说,它与我们在动画块中的帧所做的以前的动画更相似。我们只需要调用layoutIfNeeded两次 - 在动画之前和动画之后:

[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

}];

答案 2 :(得分:10)

我尝试了@ Centurion的方法,但不知何故,如果从故事板加载,我的视图会动画到错误的帧。如果我用layoutIfNeeded替换第一个updateConstraintsIfNeeded,问题就会消失,尽管我不知道为什么。如果有人能给出解释,那将非常感激。

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

答案 3 :(得分:4)

我遇到了类似的问题,这个帖子对于克服这个问题非常有帮助。

来自erurainon的回答让我走上正轨,但我想提出一个稍微不同的答案。来自erurainon的建议代码对我来说不起作用,因为我仍然有一个跳跃而不是动画过渡。 cnotethegr8提供的链接给了我工作答案:

自动布局指南 https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html(一直到页面底部)。

与erurainon的回答有些不同:

  1. 在调用动画方法之前调用容器视图上的layoutIfNeeded(而不是myView上的setNeedsUpdateConstraints)。
  2. 在动画块中设置新约束。
  3. 在动画方法的容器视图中调用layoutIfNeeded(在设置约束后),而不是在myView上。
  4. 这将遵循Apple在上面链接中建议的模式。

    示例

    我想为特定视图设置动画,只需点击一下按钮即可关闭或展开它。由于我使用autolayout并且不想在代码中硬编码任何维度(在我的情况下是高度),我决定在viewDidLayoutSubviews中捕获高度。使用autolayout时,您需要使用此方法而不是viewWillAppear。由于可以多次调用viewDidLayoutSubviews,因此我使用BOOL告诉我初始化的第一次执行。

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

    调整完整操作代码段

    // 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
        // ...
    }
    

    调整小型操作代码段

    // 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
            // ...
        }
    

    如果愿意,可以使用animateWithDuration而不是transitionWithView。

    希望这有帮助。