202

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

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

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

4

4 回答 4

489

只需遵循这个确切的模式:

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

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

添加到哪里myView的视图在哪里self.heightFromTop。您的视图正在“跳跃”,因为您在动画块中所做的唯一一件事就是设置约束,这不会立即导致布局。在您的代码中,布局发生在您 set 之后的下一个运行循环中heightFromTop.constant,到那时您已经超出了动画块的范围。

在斯威夫特 2 中:

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

UIView.animateWithDuration(0.25, animations: {
   myView.layoutIfNeeded()
})
于 2012-10-17T03:25:31.227 回答
84

Apple 建议的方式有点不同(请参阅“自动布局所做的动画更改”部分中的示例)。首先你需要在动画之前调用 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

}];
于 2014-01-23T11:15:59.130 回答
14

我尝试了@Centurion 的方法,但是如果从情节提要中加载,我的视图会以某种方式动画到错误的帧。如果我将第一个替换为 ,问题就会消失layoutIfNeededupdateConstraintsIfNeeded尽管我不知道为什么。如果有人能给出解释,将不胜感激。

[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
    self.myConstraint.constant= 100;
    [self.view layoutIfNeeded];
}];
于 2014-01-24T00:59:05.523 回答
6

我遇到了类似的问题,这个线程对克服它很有帮助。

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 上调用。

这将遵循 Apple 在上面的链接中建议的模式。

一个例子

我想为特定视图设置动画,单击按钮即可关闭或展开它。由于我使用的是自动布局并且不想在代码中硬编码任何尺寸(在我的情况下为高度),我决定在 viewDidLayoutSubviews 中捕获高度。使用自动布局时,您需要使用此方法而不是 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。

希望这可以帮助。

于 2013-11-01T19:49:44.857 回答