如何动画addSubView:使用Autolayout

kad*_*dam 2 objective-c ios autolayout

设置:containerView在屏幕内有一个UIViewController(为了简单起见,我们说containerView占用整个屏幕).

问题:创建并添加overlayView一个asView作为子视图containerView并为其设置动画,以便从右侧动画到位置作为对用户操作的响应.

UIView *overlayView = [[UIView alloc] initWithFrame:containerView.frame];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:overlayView]; // Can't add constraints before inserting into view hierarchy
Run Code Online (Sandbox Code Playgroud)

做法:领带的前沿overlayView到的前沿containerView与我会打电话的约束overlayLeadingConstraint.设置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
Run Code Online (Sandbox Code Playgroud)

动画:现在到了真正的问题; overlayView从右边开始动画.第一种方法是这样的:

overlayViewLeadingConstraint.constant = 0.0;
[UIView animateWithDuration:1.0 animations:^{
    [containerView layoutIfNeeded];
}];
Run Code Online (Sandbox Code Playgroud)

但是这不起作用,因为上面的所有代码都会在同一个运行循环上执行,因此只会显示最终结果.

第二种方法:尝试将动画推迟到将来的运行循环,直到完成初始布局之后addSubview:.

dispatch_async(dispatch_get_main_queue(), ^{
    overlayViewLeadingConstraint.constant = 0.0;
    [UIView animateWithDuration:1.0 animations:^{
        [containerView layoutIfNeeded];
    }];
});
Run Code Online (Sandbox Code Playgroud)

这也不起作用,它提示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];
    }];
}];
Run Code Online (Sandbox Code Playgroud)

时间延迟很小,但它允许在动画之前完成几个运行循环,这似乎达到了预期的效果.

问题: 上述方法似乎是一种解决方法,而不是真正的解决方案.所以我想知道是否有更好的方法.我已经考虑使用托管viewControllerviewDidLayoutSubviews方法来了解何时overlayView到位并且可以启动动画,但文档明确建议不要这样做:

但是,调用此方法并不表示已调整视图子视图的各个布局.每个子视图负责调整自己的布局.

我开始认为Apple的想法是在初始化时添加所有子视图,只是隐藏那些你不需要的子视图.因此,当为子视图设置动画时,它已经是视图层次结构的成员,它与约束正确相关.

你会怎么做?任何输入都非常感谢.

rde*_*mar 5

我不确定你对一些你没有展示的东西做了什么,但这段代码对我来说很好.我在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];
    }];
}
Run Code Online (Sandbox Code Playgroud)

这动画正确.请注意,我在动画块之前调用了layoutIfNeeded.在您的情况下,这可能是必要的,也可能不是必需的,具体取决于您未显示的代码的某些方面.

我不知道使用[constraints objectAtIndex:0]你想要修改的约束是否安全; 它适用于这种情况,但我不知道是否保证了使用可视格式设置的约束的顺序.