【发布时间】:2013-01-15 22:12:14
【问题描述】:
这既是一个问题,也是一个部分解决方案。
*此处的示例项目:
https://github.com/JosephLin/TransitionTest
问题一:
使用transitionFromViewController:... 时,由toViewController 的viewWillAppear: 完成的布局不会在过渡动画开始时显示。换句话说,pre-layout view 在动画期间显示,其内容在动画结束后捕捉到 post-layout 位置。
问题 2:
如果我自定义导航栏UIBarButtonItem 的背景,则栏按钮在动画前显示为错误的大小/位置,并在动画结束时捕捉到正确的大小/位置,类似于问题 1。
为了演示这个问题,我制作了一个简单的自定义容器控制器,它可以执行一些自定义视图转换。它几乎是一个 UINavigationController 副本,它会交叉融合而不是在视图之间推送动画。
“推送”方法如下所示:
- (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 中执行此操作,因为当时它没有正确的帧。
出于演示目的,DetailViewController 在viewDidLoad、viewWillAppear 和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(右图)。
动画前后的预期视图。
但是,标签位于y=50,好像在viewWillAppear 中制作的布局在动画发生之前没有实现(左图)。但请注意,标签的背景设置为黄色(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: 被调用之后 transitionFromViewController:
问题 1 的解决方案
好的,部分解决方案部分来了。通过显式调用beginAppearanceTransition: 和endAppearanceTransition 到toViewController,视图将在过渡动画发生之前具有正确的布局:
- (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:]
但这并不能解决问题 2!
无论出于何种原因,导航栏按钮在过渡动画开始时仍然以错误的位置/大小开始。我花了很多时间试图找到正确的解决方案,但没有运气。我开始觉得这是transitionFromViewController: 或UIAppearance 或其他任何地方的错误。请,非常感谢您对这个问题提供的任何见解。谢谢!
我尝试过的其他解决方案
- 在
transitionFromViewController:之前致电[self.view addSubview:toViewController.view];
它实际上为用户提供了完全正确的结果,解决了问题 1 和 2。问题是,viewWillAppear 和 viewDidAppear 都会被调用两次!如果我想在viewDidAppear 中做一些扩展的动画或计算是有问题的。
- 在
transitionFromViewController:之前致电[toViewController viewWillAppear:YES];
我认为这与调用beginAppearanceTransition: 几乎相同。它解决了问题 1,但没有解决问题 2。另外,文档说不要直接致电 viewWillAppear!
- 使用
[UIView animateWithDuration:]代替transitionFromViewController:
像这样: [自我 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 中的布局开始(标签为绿色,y=350)。还有,cross-dissolve不如UIViewAnimationOptionTransitionCrossDissolve
【问题讨论】:
-
使用
[self.view addSubview:toViewController.view]实际上似乎是一种合理的方式(至少对于我的用例而言)。只需检查这些方法是否已在最后一秒触发。
标签: ios xcode uiviewcontroller uiappearance