如何为 addSubView 设置动画:使用自动布局
How to animate addSubView: using Autolayout
设置:在UIViewController
里面的屏幕上有一个containerView
(为了简单起见,假设containerView
占据了整个屏幕)。
问题:创建并添加一个 overlayView
作为 containerView
的子视图,并对外观进行动画处理,使其从右侧动画到相应位置对用户操作的响应。
UIView *overlayView = [[UIView alloc] initWithFrame:containerView.frame];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:overlayView]; // Can't add constraints before inserting into view hierarchy
方法:将overlayView
的前缘与containerView
的前缘联系起来,我称之为[=22] =].将 overlayLeadingConstraint.constant
设置为 containerView
的宽度,使其最初位于屏幕右侧。
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView(width)]" options:0 metrics:metrics views:views];
[containerView addConstraints:constraints];
NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0];
overlayViewLeadingConstraint.constant = containerView.frame.size.width;
// Height constraint not shown for simplicity
动画:现在进入真正的问题;动画 overlayView
从右边到位。第一种方法是这样的:
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
但这不起作用,因为上面的所有代码都将在同一个 运行 循环中执行,因此只会显示最终结果。
第二种方法:尝试将动画推迟到未来的运行循环,直到addSubview:
之后的初始布局已经发生。
dispatch_async(dispatch_get_main_queue(), ^{
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
});
这也不起作用,它暗示 addSubview:
和约束的设置可以占用多个 运行 循环。
第三种方法:尝试进一步延迟动画:几个 运行 循环到未来。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
}];
时间延迟很小,但它允许几个 运行 循环在动画之前完成,这似乎达到了预期的效果。
问题:
上述方法似乎是一种解决方法,而不是 真正的 解决方案。所以我想知道是否有更好的方法来解决这个问题。我考虑过使用托管 viewController
的 viewDidLayoutSubviews
方法来了解 overlayView
何时到位并且可以启动动画,但文档明确建议不要这样做:
However, this method being called does not indicate that the individual layouts
of the view's subviews have been adjusted. Each subview is responsible for
adjusting its own layout.
我开始认为 Apple 的想法是在初始化时添加所有子视图,然后隐藏那些您不需要立即使用的子视图。因此,当需要为子视图设置动画时,它已经是视图层次结构的成员,并与约束正确绑定。
你会怎么做?非常感谢任何输入。
您还没有向 animations
块添加任何动画。 animations
块通常被认为是"I want my UI to end up in this state; animate stuff to make that happen"块。
[UIView animateWithDuration:1.0 animations:^{
overlayViewLeadingConstraint.constant = 0.0;
}];
我不确定您对某些未显示的内容做了什么,但这段代码对我来说效果很好。我在 IB (containerView) 中制作了一个 200 x 200 的视图,并使用了这段代码,
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *overlayView = [UIView new];
overlayView.backgroundColor = [UIColor redColor];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:overlayView];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}];
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}];
[containerView addConstraints:constraints];
[containerView addConstraints:constraints2];
NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0];
overlayViewLeadingConstraint.constant = containerView.frame.size.width;
[containerView layoutIfNeeded];
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
}
此动画正确。请注意,我在动画块之前调用了 layoutIfNeeded。在您的情况下,这可能是必要的,也可能不是必要的,具体取决于您未显示的代码的某些方面。
不知道用[constraints objectAtIndex:0]
获取你要修改的约束是否安全;在这种情况下它起作用了,但我不知道用视觉格式设置的约束的顺序是否得到保证。
设置:在UIViewController
里面的屏幕上有一个containerView
(为了简单起见,假设containerView
占据了整个屏幕)。
问题:创建并添加一个 overlayView
作为 containerView
的子视图,并对外观进行动画处理,使其从右侧动画到相应位置对用户操作的响应。
UIView *overlayView = [[UIView alloc] initWithFrame:containerView.frame];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:overlayView]; // Can't add constraints before inserting into view hierarchy
方法:将overlayView
的前缘与containerView
的前缘联系起来,我称之为[=22] =].将 overlayLeadingConstraint.constant
设置为 containerView
的宽度,使其最初位于屏幕右侧。
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView(width)]" options:0 metrics:metrics views:views];
[containerView addConstraints:constraints];
NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0];
overlayViewLeadingConstraint.constant = containerView.frame.size.width;
// Height constraint not shown for simplicity
动画:现在进入真正的问题;动画 overlayView
从右边到位。第一种方法是这样的:
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
但这不起作用,因为上面的所有代码都将在同一个 运行 循环中执行,因此只会显示最终结果。
第二种方法:尝试将动画推迟到未来的运行循环,直到addSubview:
之后的初始布局已经发生。
dispatch_async(dispatch_get_main_queue(), ^{
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
});
这也不起作用,它暗示 addSubview:
和约束的设置可以占用多个 运行 循环。
第三种方法:尝试进一步延迟动画:几个 运行 循环到未来。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
}];
时间延迟很小,但它允许几个 运行 循环在动画之前完成,这似乎达到了预期的效果。
问题:
上述方法似乎是一种解决方法,而不是 真正的 解决方案。所以我想知道是否有更好的方法来解决这个问题。我考虑过使用托管 viewController
的 viewDidLayoutSubviews
方法来了解 overlayView
何时到位并且可以启动动画,但文档明确建议不要这样做:
However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout.
我开始认为 Apple 的想法是在初始化时添加所有子视图,然后隐藏那些您不需要立即使用的子视图。因此,当需要为子视图设置动画时,它已经是视图层次结构的成员,并与约束正确绑定。
你会怎么做?非常感谢任何输入。
您还没有向 animations
块添加任何动画。 animations
块通常被认为是"I want my UI to end up in this state; animate stuff to make that happen"块。
[UIView animateWithDuration:1.0 animations:^{
overlayViewLeadingConstraint.constant = 0.0;
}];
我不确定您对某些未显示的内容做了什么,但这段代码对我来说效果很好。我在 IB (containerView) 中制作了一个 200 x 200 的视图,并使用了这段代码,
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *overlayView = [UIView new];
overlayView.backgroundColor = [UIColor redColor];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:overlayView];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}];
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[overlayView]|" options:0 metrics:nil views:@{@"overlayView":overlayView}];
[containerView addConstraints:constraints];
[containerView addConstraints:constraints2];
NSLayoutConstraint *overlayViewLeadingConstraint = [constraints objectAtIndex:0];
overlayViewLeadingConstraint.constant = containerView.frame.size.width;
[containerView layoutIfNeeded];
overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
[containerView layoutIfNeeded];
}];
}
此动画正确。请注意,我在动画块之前调用了 layoutIfNeeded。在您的情况下,这可能是必要的,也可能不是必要的,具体取决于您未显示的代码的某些方面。
不知道用[constraints objectAtIndex:0]
获取你要修改的约束是否安全;在这种情况下它起作用了,但我不知道用视觉格式设置的约束的顺序是否得到保证。