自定义容器控制器:transitionFromViewController:在动画之前查看未正确布局 [英] Custom Container Controller: transitionFromViewController: View not properly layed-out before animation

查看:117
本文介绍了自定义容器控制器:transitionFromViewController:在动画之前查看未正确布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个问题和部分解决方案。

This is both a question and a partial solution.

*此处的示例项目:

https://github.com/JosephLin/TransitionTest

使用 transitionFromViewController:... ,由 toViewController完成的布局 viewWillAppear:在转换时不会显示动画开始了。换句话说,在动画期间显示预布局视图,并且它的内容会在动画后捕捉到布局后位置。

When using transitionFromViewController:..., layouts done by the toViewController's viewWillAppear: doesn't show up when the transition animation begins. In other words, the pre-layout view shows during the animation, and it's contents snap to the post-layout positions after the animation.

如果我自定义导航栏的 UIBarButtonItem 的背景,则条形按钮会显示错误的大小/动画前的位置,并在动画结束时捕捉到正确的大小/位置,类似于问题1.

If I customize the background of my navbar's UIBarButtonItem, the bar button shows up with the wrong size/position before the animation, and snaps to the correct size/position when the animation ends, similar to Problem 1.

为了演示这个问题,我做了一个裸骨的自定义容器控制器来执行一些自定义视图转换。它几乎是一个UINavigationController副本,它在视图之间进行交叉溶解而不是推送动画。

To demonstrate the problem, I made a bare-bone custom container controller that does some custom view transitions. It's pretty much a UINavigationController copy that does cross-dissolve instead of push animation between views.

'Push'方法如下所示:

The 'Push' method looks like this:

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];
    toViewController.view.frame = self.view.bounds;

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                                [toViewController didMoveToParentViewController:self];
                            }];
}

现在, DetailViewController (我正在推动的视图控制器)需要在 viewWillAppear:中布局其内容。它不能在 viewDidLoad 中执行,因为它当时没有正确的帧。

Now, DetailViewController (the view controller I'm pushing to) needs to layout its content in viewWillAppear:. It can't do it in viewDidLoad because it wouldn't have the correct frame at that time.

For演示目的, DetailViewController 将其标签设置为 viewDidLoad 中的不同位置和颜色, viewWillAppear viewDidAppear

For demonstration purpose, DetailViewController sets its label to different locations and colors in viewDidLoad, viewWillAppear, and viewDidAppear:

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 50;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewDidLoad";
    self.descriptionLabel.backgroundColor = [UIColor redColor];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 200;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewWillAppear";
    self.descriptionLabel.backgroundColor = [UIColor yellowColor];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"%s", __PRETTY_FUNCTION__);
    CGRect rect = self.descriptionLabel.frame;
    rect.origin.y = 350;
    self.descriptionLabel.frame = rect;
    self.descriptionLabel.text = @"viewDidAppear";
    self.descriptionLabel.backgroundColor = [UIColor greenColor];
}






现在,当推动 DetailViewController ,我期待在动画开始时看到 y = 200 处的标签(左图) ,然后在动画结束后跳转到 y = 350 (右图)。


Now, when pushing the DetailViewController, I'm expecting to see the label at y =200 at the begining of the animation (left image), and then jumps to y = 350 after the animation is finished (right image).


动画前后的预期视图。

但是,标签位于 y = 50 ,就好像在 viewWillAppear 中进行的布局没有在动画发生了(左图)。但请注意标签的背景设置为黄色(由 viewWillAppear 指定的颜色)!

However, the label was at y=50, as if the layout made in viewWillAppear didn't make it before the animation took place (left image). But notice that the label's background was set to yellow (the color specified by viewWillAppear)!


动画开头的布局错误。请注意,条形按钮也以错误的位置/大小开始。

控制台日志

TransitionTest [49795:c07] - [DetailViewController viewDidLoad]

TransitionTest [49795:c07]在transitionFromViewController之前:

TransitionTest [49795:c07] - [DetailViewController viewWillAppear:]

TransitionTest [49795:c07] - [DetailViewController viewWillLayoutSubviews]

TransitionTest [49795:c07] - [DetailViewController viewDidLayoutSubviews]

TransitionTest [49795:c07] - [DetailViewController viewDidAppear:]

请注意 viewWillAppear:被称为 AFTER transitionFromViewController:

Console Log
TransitionTest[49795:c07] -[DetailViewController viewDidLoad]
TransitionTest[49795:c07] Before transitionFromViewController:
TransitionTest[49795:c07] -[DetailViewController viewWillAppear:]
TransitionTest[49795:c07] -[DetailViewController viewWillLayoutSubviews]
TransitionTest[49795:c07] -[DetailViewController viewDidLayoutSubviews]
TransitionTest[49795:c07] -[DetailViewController viewDidAppear:]
Notice that viewWillAppear: was called AFTER transitionFromViewController:

好的,这里是部分解决方案部分。通过显式调用 beginAppearanceTransition: endAppearanceTransition toViewController ,在转换动画发生之前,view将具有正确的布局:

Alright, here comes the partial solution part. By explicitly calling beginAppearanceTransition: and endAppearanceTransition to toViewController, the view will have the correct layout before the transition animation takes place:

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];
    toViewController.view.frame = self.view.bounds;

    [toViewController beginAppearanceTransition:YES animated:NO];

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                                [toViewController didMoveToParentViewController:self];
                                [toViewController endAppearanceTransition];
                            }];
}

请注意 viewWillAppear:现在被称为BEFORE transitionFromViewController:
TransitionTest [18398:c07] - [DetailViewController viewDidLoad]

TransitionTest [18398:c07] - [DetailViewController viewWillAppear:]

TransitionTest [18398:c07]在transitionFromViewController之前:

TransitionTest [18398:c07] - [DetailViewController viewWillLayoutSubviews]

TransitionTest [18398:c07] - [DetailViewController viewDidLayoutSubviews]

TransitionTest [18398:c07] - [DetailViewController viewDidAppear:]

Notice that viewWillAppear: is now called BEFORE transitionFromViewController: TransitionTest[18398:c07] -[DetailViewController viewDidLoad]
TransitionTest[18398:c07] -[DetailViewController viewWillAppear:]
TransitionTest[18398:c07] Before transitionFromViewController:
TransitionTest[18398:c07] -[DetailViewController viewWillLayoutSubviews]
TransitionTest[18398:c07] -[DetailViewController viewDidLayoutSubviews]
TransitionTest[18398:c07] -[DetailViewController viewDidAppear:]

无论出于何种原因,导航栏按钮仍然以过渡动画开头的位置/大小错误。我花了很多时间试图找到正确的解决方案,但没有运气。我开始觉得这是 transitionFromViewController: UIAppearance 或其他什么的错误。请非常感谢您对此问题提供的任何见解。谢谢!

For whatever reason, the navbar buttons still begin with the wrong position/size at the beginning of the transition animation. I spent so many time trying to find THE right solution but without luck. I'm starting to feel it's a bug in transitionFromViewController: or UIAppearance or whatever. Please, any insight you can offer to this question is greatly appreciated. Thanks!

我尝试过的其他解决方案


  • transitionFromViewController之前调用 [self.view addSubview:toViewController.view];

它实际上为用户提供了正确的结果,修复了问题1和2。问题是, viewWillAppear viewDidAppear 都将被调用两次!如果我想在 viewDidAppear 中做一些广阔的动画或计算,这是有问题的。

It actually gives exactly the right result to the user, fixes both Problem 1&2. The problem is, viewWillAppear and viewDidAppear will both be called twice! It's problematic if I want to do some expansive animation or calculation in viewDidAppear.


  • transitionFromViewController之前调用 [toViewController viewWillAppear:YES];

我认为这与调用 beginAppearanceTransition:几乎相同。它修复了问题1,但没有解决问题2.此外,文档说不直接调用 viewWillAppear

I think it's pretty much the same as calling beginAppearanceTransition:. It fixes Problem 1 but not Problem 2. Plus, the doc says not to call viewWillAppear directly!


  • 使用 [UIView animateWithDuration:] 而不是 transitionFromViewController:

  • Use [UIView animateWithDuration:] instead of transitionFromViewController:

像这样:
[self addChildViewController:toViewController];
[self.view addSubview:toViewController.view];
toViewController.view.alpha = 0.0;

Like this: [self addChildViewController:toViewController]; [self.view addSubview:toViewController.view]; toViewController.view.alpha = 0.0;

[UIView animateWithDuration:0.5 animations:^{
    toViewController.view.alpha = 1.0;
} completion:^(BOOL finished) {
    [toViewController didMoveToParentViewController:self];
}];

它修复了问题2,但视图以 viewDidAppear <中的布局开始/ code>(标签为绿色,y = 350)。此外,交叉溶解不如使用 UIViewAnimationOptionTransitionCrossDissolve

It fixes Problem 2, but the view started with the layout in viewDidAppear (label is green, y=350). Also, the cross-dissolve is not as good as using UIViewAnimationOptionTransitionCrossDissolve

推荐答案

好吧,在toViewController.view中添加layoutIfNeeded似乎可以解决问题 - 这会在视图显示在屏幕上之前正确显示视图(没有添加/删除),并且不再有奇怪的双重viewDidAppear:call。

Ok, adding layoutIfNeeded to the toViewController.view seems to do the trick - this gets the view laid out properly before it shows up on screen (without the add/remove), and no more weird double viewDidAppear: call.

- (void)pushController:(UIViewController *)toViewController
{
    UIViewController *fromViewController = [self.childViewControllers lastObject];

    [self addChildViewController:toViewController];

    toViewController.view.frame = self.view.bounds;
    [toViewController.view layoutIfNeeded];

    NSLog(@"Before transitionFromViewController:");
    [self transitionFromViewController:fromViewController
                      toViewController:toViewController
                              duration:0.5
                               options:UIViewAnimationOptionTransitionCrossDissolve
                            animations:^{}
                            completion:^(BOOL finished) {
                            }];

}

这篇关于自定义容器控制器:transitionFromViewController:在动画之前查看未正确布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆