UIViewController – 问题与自定义解散转换

概要

我有一个内容UIViewController提供了一个设置UIViewController使用自定义转换。 演示文稿是presentViewController:animated:completion:

当我后来用dismissViewControllerAnimated:completion:closures设置时,呈现控制器在设置控制器呈现之前突然跳回到它的初始位置。

我有一个解决这个在设备上,但不是模拟器。 但是,我想知道我在做什么错,而不是在一个让它消失的闪光。 我也打算让这个animation互动,我怀疑这个问题会放大。

自定义过渡 – 打开引擎盖

期望的效果是呈现控制器沿着屏幕向下滑动,并且看到所呈现的控制器从其抬起以填满屏幕的位置处在它后面。 呈现控制器的顶部在所呈现的控制器的使用寿命期间保持在屏幕上。 它停留在屏幕的底部,但所呈现的控制器之上

您可以想象,将汽车上的发动机罩(前部控制器)抬起,可以看到后面的发动机(所显示的设置),但发动机罩在底部保持可见状态。

我打算改进这一点,以便呈现控制器真的能以3D的方式提升视angular,但是我还没有那么远。

当设置被解除时,原来的提示控制器(阀盖)应该滑回屏幕,并且所呈现的控制器(设置)稍微后退(closures阀盖)。

这是在屏幕上和屏幕之间切换设置的方法(它只是由UIButton调用)。 您会注意到呈现视图控制器将自己设置为<UIViewControllerTransitioningDelegate>

 -(void) toggleSettingsViewController { const BOOL settingsAreShowing = [self presentedViewController] != nil; if(!settingsAreShowing) { UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"]; [settingsController setTransitioningDelegate: self]; [settingsController setModalPresentationStyle: UIModalPresentationCustom]; [self presentViewController: settingsController animated: YES completion: nil]; } else { [self dismissViewControllerAnimated: YES completion: nil]; } } 

为了实现<UIViewControllerAnimatedTransitioning>呈现视图控制器也只是返回自己作为<UIViewControllerAnimatedTransitioning>

 -(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return self; } -(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed { // Test Point 1. return self; } 

所以最后,呈现视图控制器将收到animateTransition: ::

 -(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; const BOOL isUnwinding = [toController presentedViewController] == fromController; const BOOL isPresenting = !isUnwinding; UIViewController * presentingController = isPresenting ? fromController : toController; UIViewController * presentedController = isPresenting ? toController : fromController; if(isPresenting) { // Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller. [[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]]; // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed. presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9); presentedController.view.alpha = 0.7; [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{ // Lift up the presented controller. presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0); // Brighten the presented controller (out of shadow). presentedController.view.alpha = 1; // Push the presenting controller down the screen – 3d effect to be added later. presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0); } completion: ^(BOOL finished){ [transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; }]; } else { // Test Point 2. // !!!This line should not be needed!!! // It resets the presenting controller to where it ought to be anyway. presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0); [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{ // Bring the presenting controller back to its original position. presentingController.view.layer.transform = CATransform3DIdentity; // Lower the presented controller again and put it back in to shade. presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9); presentedController.view.alpha = 0.4; } completion:^(BOOL finished) { [transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; }]; } } -(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { return 0.5; } 

问题

在上面的代码中,我已经指出了!!!这行不应该需要!

发生什么事是在testing点1testing点2之间,呈现视图控制器的屏幕位置被重置为默认的全屏界限。 所以,而不是在屏幕的底部准备好再次顺利地进行animation的回放,突然跳起来屏幕的位置也就是平滑的animation!

我已经尝试了各种方法来在屏幕上向下呈现视图控制器:

  • 我改变了观点的框架。
  • 我改变了观点的转变。
  • 我已经改变了它的视图的3D变换。

在所有情况下,在testing点1 ,当转换代表被要求时,呈现控制器被设置为我所期望的。 但是,在所有情况下,在testing点2 ,呈现视图控制器已经失去了正确的位置,并已被“清除”以具有正常的全屏幕位置,我想使其animation。

在上面的工作中,我显式地将呈现的视图控制器重新定位到它应该在animation开始的位置!!!这行不应该是需要的! 。 这似乎在iOS 7当前版本的设备上工作。但是,在模拟器上,控制器在至less一个帧的清除位置处可见。

我怀疑我正在做其他的事情,而且我会陷入麻烦,只能掩盖另一个问题。

任何想法是怎么回事? 谢谢!

使用自定义过渡animation解除模态表示视图控制器的一些潜在问题:

  • 将呈现(“到”)视图添加到容器,然后将呈现的视图放在前面。 不要添加呈现视图,因为您可能会将其从当前的超级视图中移除。
  • 取消之后,UIKit会在调用animateTransition之前将呈现视图的alpha设置为0。 所以你需要将它设置为1.0或者在完成当前的任何事情之后才开始closures你的closuresanimation。
  • 同样,对于所呈现的视图的变换。 在解散之前,它会在调用animateTransition之前重置为身份。

鉴于这一切,我认为这应该工作:

 -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView *containerView = transitionContext.containerView; const BOOL isUnwinding = [toController presentedViewController] == fromController; const BOOL isPresenting = !isUnwinding; UIViewController *presentingController = isPresenting ? fromController : toController; UIViewController *presentedController = isPresenting ? toController : fromController; [containerView addSubview:presentingController.view]; [containerView bringSubviewToFront:presentingController.view]; if(isPresenting) { // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed. presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9); presentedController.view.alpha = 0.7; [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{ // Lift up the presented controller. presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0); // Brighten the presented controller (out of shadow). presentedController.view.alpha = 1; // Push the presenting controller down the screen – 3d effect to be added later. presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0); } completion: ^(BOOL finished){ [transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; }]; } else { presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9); presentedController.view.alpha = 0.7; [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{ // Bring the presenting controller back to its original position. presentingController.view.layer.transform = CATransform3DIdentity; // Lower the presented controller again and put it back in to shade. presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9); presentedController.view.alpha = 0.4; } completion:^(BOOL finished) { [transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; }]; } } 

最初,我想到了使用CATransition时有CATransition自定义过渡效果presentViewController:animated:completion:dismissViewControllerAnimated:completion:视图控制器。 但是当你想要显示视图控制器的一部分时,那么我想CATransition不会有什么帮助,因为你不能完全控制你想要移动视图控制器的距离。

我认为最简单的方法是有两个全屏UIView一个单一的视图控制器。 对于第一个UIView(视图控制器的视图,即self.view),您可以设置该设置,在第二个UIView上,它是常规视图。 在ViewDidLoad中,使用[self.view addSubview:2ndView];添加第二个视图[self.view addSubview:2ndView]; 。 稍后当您想要显示设置视图时,可以这样做

 CGRect frame = secondView.frame; frame.origin.y = the_y_coordinate_you_like; UIView animateWithDuration:0.2 animations:^{ secondView.frame = frame; }]; 

然后做另一种方式把2ndView带回来。