NSLayoutConstraints是可动画的吗?

我试图动画一些观点,使他们被巨大的键盘挡在景观。它工作得很好,如果我只是简单地动画帧,但其他人认为这是反效果的,我应该更新NSLayoutConstraints代替。然而,它们似乎没有动画效果。有人让他们成功工作了吗?

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

结果就是瞬间跳到了刚才提到的高度。

56823 次浏览

只要遵循这个模式:

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


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

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

在Swift 2中:

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


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

我也有类似的问题,这条线索是一个很大的帮助,让它过去。

erurainon的答案让我找到了正确的方向,但我想提出一个稍微不同的答案。来自erurainon的建议代码并不适合我,因为我仍然有一个跳跃而不是一个动画过渡。由cnotethegr8提供的链接给了我工作的答案:

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

与erurainon的答案有几点不同:

  1. 在调用动画方法之前调用容器视图上的layoutIfNeeded(而不是myView上的setNeedsUpdateConstraints)。
  2. 在动画块中设置新的约束。
  3. 在animations方法的容器视图上调用layoutIfNeeded(在设置约束之后),而不是在myView上。

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

一个例子

我想要动画一个特定的视图,在点击按钮时关闭或展开它。由于我使用自动布局,并不想硬编码任何尺寸(在我的情况下高度)在代码中,我决定在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。

希望这能有所帮助。

苹果建议的方式有点不同(参见“通过自动布局进行动画化更改”部分中的示例)。首先,你需要在动画之前调用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


}];

我尝试了@百夫长的方法,但不知怎的,我的视图会动画到一个错误的帧,如果它是从故事板加载。如果我用updateConstraintsIfNeeded替换第一个layoutIfNeeded,这个问题就消失了,尽管我不知道为什么。如果有人能解释一下,我将不胜感激。

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