这是一个问题和部分解决方案。
*示例项目:
https://github.com/JosephLin/TransitionTest
使用transitionFromViewController:...
时,toViewController
的{{1}}完成的布局在转换动画开始时不会显示。换句话说,在动画期间显示预布局视图,并且它的内容会在动画后捕捉到布局后的位置。
如果我自定义导航栏viewWillAppear:
的背景,则在动画播放前,条形按钮会显示错误的大小/位置,并在动画结束时捕捉到正确的大小/位置,类似于问题1。
为了演示这个问题,我制作了一个裸骨的自定义容器控制器来执行一些自定义视图转换。它几乎是一个UINavigationController副本,它在视图之间进行交叉溶解而不是推送动画。
“推送”方法如下所示:
UIBarButtonItem
现在,- (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
指定的颜色)!
动画开头的布局错误。请注意,条形按钮也以错误的位置/大小开始。
控制台日志
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 viewWillAppear:
好的,这里是部分解决方案部分。通过明确调用transitionFromViewController:
和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:
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:]
无论出于何种原因,导航按钮在转换动画开始时仍以错误的位置/大小开头。我花了很多时间试图找到正确的解决方案,但没有运气。我开始觉得这是transitionFromViewController:
或transitionFromViewController:
或其他什么的错误。请非常感谢您对此问题提供的任何见解。谢谢!
我尝试过的其他解决方案
UIAppearance
[self.view addSubview:toViewController.view];
它实际上为用户提供了正确的结果,修复了问题1和2。问题是,transitionFromViewController:
和viewWillAppear
都会被调用两次!如果我想在viewDidAppear
中进行一些广泛的动画或计算,那就有问题了。
viewDidAppear
[toViewController viewWillAppear:YES];
我认为这与调用transitionFromViewController:
几乎相同。它修复了问题1,但没有修复问题2.此外,文档说不要直接调用beginAppearanceTransition:
!
viewWillAppear
代替[UIView animateWithDuration:]
像这样: [self addChildViewController:toViewController]; [self.view addSubview:toViewController.view]; toViewController.view.alpha = 0.0;
transitionFromViewController:
它解决了问题2,但视图以[UIView animateWithDuration:0.5 animations:^{
toViewController.view.alpha = 1.0;
} completion:^(BOOL finished) {
[toViewController didMoveToParentViewController:self];
}];
中的布局开始(标签为绿色,y = 350)。此外,交叉溶解不如使用viewDidAppear
答案 0 :(得分:7)
好的,在toViewController.view中添加layoutIfNeeded似乎可以解决问题 - 这会在视图显示在屏幕上之前正确显示视图(没有添加/删除),并且不再有奇怪的双重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) {
}];
}
答案 1 :(得分:2)
遇到同样的问题,您只需要转发外观事务和UIView.Animate。这种方法解决了所有问题,但没有创建新问题。这是一些C#代码(xamarin):
var fromView = fromViewController.View;
var toView = toViewController.View;
fromViewController.WillMoveToParentViewController(null);
AddChildViewController(toViewController);
fromViewController.BeginAppearanceTransition(false, true);
toViewController.BeginAppearanceTransition(true, true);
var frame = fromView.Frame;
frame.X = -viewWidth * direction;
toView.Frame = frame;
View.Add(toView);
UIView.Animate(0.3f,
animation: () =>
{
toView.Frame = fromView.Frame;
fromView.MoveTo(x: viewWidth * direction);
},
completion: () =>
{
fromView.RemoveFromSuperview();
fromViewController.EndAppearanceTransition();
toViewController.EndAppearanceTransition();
fromViewController.RemoveFromParentViewController();
toViewController.DidMoveToParentViewController(this);
}
);
当然你应该禁用外观方法的自动转发并手动完成:
public override bool ShouldAutomaticallyForwardAppearanceMethods
{
get { return false; }
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
CurrentViewController.BeginAppearanceTransition(true, animated);
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
CurrentViewController.EndAppearanceTransition();
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
CurrentViewController.BeginAppearanceTransition(false, animated);
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
CurrentViewController.EndAppearanceTransition();
}