在 viewDidLayoutSubviews 中更改框架 [英] Changing a frame in viewDidLayoutSubviews
问题描述
当第一次显示视图控制器的视图时,我想运行一个动画,其中视图控制器中的所有元素从屏幕底部的外部滑动到它们的自然位置.为了实现这一点,我在 viewDidLayoutSubviews
中执行了 subview.frame.origin.y += self.view.frame.size.height
.我也试过 viewWillAppear
,但它根本不起作用.然后我在 viewDidAppear
中使用 subview.frame.origin.y -= self.view.frame.size.height
将它们动画到它们的自然位置.
When a view controller's view is first shown I want to run an animation in which all elements in the view controller slide from outside the bottom of the screen to their natural positions. To achieve this, I do subview.frame.origin.y += self.view.frame.size.height
in viewDidLayoutSubviews
. I also tried viewWillAppear
, but it doesn't work at all. Then I animate them up to their natural positions with subview.frame.origin.y -= self.view.frame.size.height
in viewDidAppear
.
问题在于 viewDidLayoutSubviews
在视图控制器的整个生命周期中被多次调用.因此,当显示键盘之类的事情发生时,我的所有内容都会再次被替换到视图之外.
The problem is that viewDidLayoutSubviews
is called several times throughout the view controller's lifespan. As such, when things like showing the keyboard happen all my content gets replaced outside the view again.
有没有更好的方法来做到这一点?我是否需要添加某种标志来检查动画是否已经运行?
Is there a better method for doing this? Do I need to add some sort of flag to check whether the animation has already run?
这是代码.在这里,我在 viewDidLayoutSubviews
中调用 prepareAppearance
,这是有效的,但是 viewDidLayoutSubviews
在控制器的整个生命周期中被多次调用.
here's the code. Here I'm calling prepareAppearance
in viewDidLayoutSubviews
, which works, but viewDidLayoutSubviews
is called multiple times throughout the controller's life span.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self prepareAppearance];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self animateAppearance];
}
- (NSArray *)animatableViews
{
return @[self.createAccountButton, self.facebookButton, self.linkedInButton, self.loginButton];
}
- (void)prepareAppearance
{
NSArray * views = [self animatableViews];
NSUInteger count = [views count];
for (NSUInteger it=0 ; it < count ; ++it) {
UIView * view = [views objectAtIndex:it];
CGRect frame = view.frame;
// Move the views outside the screen, to the bottom
frame.origin.y += self.view.frame.size.height;
[view setFrame:frame];
}
}
- (void)animateAppearance
{
NSArray * views = [self animatableViews];
NSUInteger count = [views count];
for (NSUInteger it=0 ; it < count ; ++it) {
__weak UIView * weakView = [views objectAtIndex:it];
CGRect referenceFrame = self.view.frame;
[UIView animateWithDuration:0.4f
delay:0.05f * it
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect frame = weakView.frame;
frame.origin.y -= referenceFrame.size.height;
[weakView setFrame:frame];
}
completion:^(BOOL finished) {
}];
}
}
推荐答案
如果您需要在视图出现时设置动画,然后不触摸子视图,我建议如下:
If you need to animate something when view will appear and then not touch subviews later, I would suggest the following:
- 不要改变/触摸
viewDidLayoutSubviews
- 在
viewWillAppear
中添加将元素移动到屏幕外的逻辑(到它们的初始位置动画之前) - 添加逻辑以在
viewDidAppear
中将元素动画到其适当位置
- Don't change/touch
viewDidLayoutSubviews
- Add logic to move elements outside the screen (to their initial position before animation) in
viewWillAppear
- Add logic to animate elements into their proper position in
viewDidAppear
更新:
如果您使用自动布局(这是非常好的事情),则不能通过直接更改其框架来为视图设置动画(因为自动布局会忽略并再次更改它们).您需要做的是将插座暴露给负责 Y 位置的约束(在您的情况下)并更改该约束而不是设置框架.
If you're using auto-layout (which is very good thing), you can't animate views by changing their frames directly (because auto-layout would ignore that and change them again). What you need to do is to expose outlets to constraints responsible for Y-position (in your case) and change that constraints rather then setting frames.
在更新动画方法中的约束后,也不要忘记包含对 [weakView layoutIfNeeded]
的调用.
Also don't forget to include call to [weakView layoutIfNeeded]
after you update constraints in the animation method.
这篇关于在 viewDidLayoutSubviews 中更改框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!