ios NSLayoutConstraints 是可动画的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12926566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are NSLayoutConstraints animatable?
提问by borrrden
I am trying to animate up some views so that they are blocked by the giant keyboard in landscape. It works well if I simply animate the frames, but others have suggested that this is counter-productive and I should be updating the NSLayoutConstraints instead. However, they don't seem to be animatable. Has anyone gotten them to work with success?
我正在尝试为一些视图设置动画,以便它们被横向的巨型键盘挡住。如果我只是为帧设置动画效果很好,但其他人认为这会适得其反,我应该改为更新 NSLayoutConstraints。但是,它们似乎不能设置动画。有没有人让他们成功地工作?
//heightFromTop is an NSLayoutConstraint referenced from IB
[UIView animateWithDuration:0.25 animations:^{
self.heightFromTop.constant= 550.f;
}];
The result is an instant jump to the height in question.
结果是立即跳到了有问题的高度。
回答by John Estropia
Just follow this exact pattern:
只需遵循以下确切模式:
self.heightFromTop.constant = 550.0f;
[myView setNeedsUpdateConstraints];
[UIView animateWithDuration:0.25f animations:^{
[myView layoutIfNeeded];
}];
where myView
is the view where self.heightFromTop
was added to. Your view is "jumping" because the only thing you did in the animation block was to set the constraint, which does not cause layouts immediately. In your code, the layout happens on the next run loop after you set heightFromTop.constant
, and by that time you are already outside the scope of the animation block.
其中myView
是其中视图self.heightFromTop
加入。您的视图正在“跳跃”,因为您在动画块中所做的唯一一件事就是设置约束,这不会立即导致布局。在您的代码中,布局发生在您 set 之后的下一个运行循环中heightFromTop.constant
,到那时您已经超出了动画块的范围。
In Swift 2:
在 Swift 2 中:
self.heightFromTop.constant = 550
myView.setNeedsUpdateConstraints()
UIView.animateWithDuration(0.25, animations: {
myView.layoutIfNeeded()
})
回答by Centurion
The Apple's suggested way is a little bit different (See example in "Animating Changes Made by Auto Layout" section). First you need to call layoutIfNeeded before the animation. Then add your animation stuff inside the animation block and then call layoutIfNeeded at the end again. For the guys like me who are transitioning to autolayout, its more similar way to previous animations that we were doing with the frames inside animation blocks. We just need to call layoutIfNeeded twice - before animations and after animations:
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
}];
回答by Joseph Lin
I tried @Centurion's approach, but somehow my view would animate to a wrong frame if it's loaded from the storyboard. The issue goes away if I replace the first layoutIfNeeded
with updateConstraintsIfNeeded
, though I have no idea why. If anyone can give an explanation it'd be much appreciated.
我尝试了@Centurion 的方法,但不知何故,如果我的视图是从故事板加载的,它就会动画到错误的帧。如果我用 替换第一个layoutIfNeeded
,问题就会消失updateConstraintsIfNeeded
,尽管我不知道为什么。如果有人可以给出解释,将不胜感激。
[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
self.myConstraint.constant= 100;
[self.view layoutIfNeeded];
}];
回答by Scott Carter
I was having a similar problem and this thread was a great help to get past it.
我遇到了类似的问题,这个线程对解决它有很大帮助。
The answer from erurainon put me on the right track, but I'd like to propose a slightly different answer. The suggested code from erurainon did not work for me, as I still had a jump instead of an animated transition. The link provided by cnotethegr8 gave me the working answer:
erurainon 的回答让我走上了正轨,但我想提出一个稍微不同的答案。来自 erurainon 的建议代码对我不起作用,因为我仍然有一个跳跃而不是动画过渡。cnotethegr8 提供的链接给了我有效的答案:
Auto Layout Guidehttps://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html(all the way to the bottom of the page).
自动布局指南https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html(一直到页面底部)。
A few differences from the answer by erurainon:
与 erurainon 的回答有一些不同:
- Call layoutIfNeeded on the container view before the call to an animation method (and instead of setNeedsUpdateConstraints on myView).
- Set the new constraint in the animations block.
- Call layoutIfNeeded on the container view in the animations method (after setting the constraint), instead of on myView.
- 在调用动画方法之前在容器视图上调用 layoutIfNeeded(而不是在 myView 上调用 setNeedsUpdateConstraints)。
- 在动画块中设置新约束。
- 在动画方法中(设置约束后)在容器视图上调用 layoutIfNeeded,而不是在 myView 上。
This will adhere to the pattern suggested by Apple in the link above.
这将遵循 Apple 在上面链接中建议的模式。
An example
一个例子
I wanted to animate a particular view, closing or expanding it at the click of a button. Since I'm using autolayout and didn't want to hard code any dimensions (in my case height) in the code, I decided to capture the height in viewDidLayoutSubviews. You need to use this method and not viewWillAppear when using autolayout. Since viewDidLayoutSubviews may be called many times, I used a BOOL to let me know about the first execution for my initialization.
我想为特定视图设置动画,单击按钮即可关闭或展开它。由于我使用自动布局并且不想在代码中硬编码任何尺寸(在我的情况下为高度),我决定在 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];
}
}
Resize full action snippet
调整完整动作片段的大小
// 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
// ...
}
Resize small action snippet
调整小动作片段的大小
// 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
// ...
}
You can use animateWithDuration instead of transitionWithView if you wish.
如果您愿意,您可以使用 animateWithDuration 而不是 transitionWithView。
Hope this helps.
希望这可以帮助。