“从视图控制器”消失使用UIViewControllerContextTransitioning

我有一个问题,我已经在下面描述。

我正在使用UIViewControllerContextTransitioning自定义转换。

我有2个视图控制器,第一个视图控制器和第二个视图控制器。

现在我想在animation的第一个视图控制器上添加secondview控制器。 我已经实现了,现在我有了secondview控制器透明,所以我们可以看到secondview控制器下面的第一个视图控制器。

但是我不能够看到第一个视图控制器,我只能看到第二个视图控制器下面的黑屏。

这是代码。

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{ self.transitionContext = transitionContext; if(self.isPresenting){ [self executePresentationAnimation:transitionContext]; } else{ [self executeDismissalAnimation:transitionContext]; } } -(void)executePresentationAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{ UIView* inView = [transitionContext containerView]; UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; CGRect offScreenFrame = inView.frame; offScreenFrame.origin.y = inView.frame.size.height; toViewController.view.frame = offScreenFrame; toViewController.view.backgroundColor = [UIColor clearColor]; fromViewController.view.backgroundColor = [UIColor clearColor]; inView.backgroundColor = [UIColor clearColor]; [inView insertSubview:toViewController.view aboveSubview:fromViewController.view]; // [inView addSubview:toViewController.view]; CFTimeInterval duration = self.presentationDuration; CFTimeInterval halfDuration = duration/2; CATransform3D t1 = [self firstTransform]; CATransform3D t2 = [self secondTransformWithView:fromViewController.view]; [UIView animateKeyframesWithDuration:halfDuration delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{ [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.5f animations:^{ fromViewController.view.layer.transform = t1; }]; [UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.5f animations:^{ fromViewController.view.layer.transform = t2; }]; } completion:^(BOOL finished) { }]; [UIView animateWithDuration:duration delay:(halfDuration - (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{ toViewController.view.frame = inView.frame; } completion:^(BOOL finished) { [self.transitionContext completeTransition:YES]; }]; } 

[self.transitionContext completeTransition:YES]; 称为,突然第一个视图控制器消失,黑屏显示在第二个视图控制器下面。

有没有想法? 谢谢。

我在这里遇到同样的问题 – 看起来像iOS 8中的一个bug。我已经提交了一个雷达 。

在屏幕变黑之后,我使用Reveal来检查视图层次结构。 关键的UIWindow是完全空的 – 根本没有视图层次!

Reveal'd

我玩了一下,看起来有一个简单的解决方法,简单的情况下。 你可以重新添加toViewController的视图作为关键窗口的子视图:

 transitionContext.completeTransition(true) UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view) 

我已经检查和关键窗口的rootViewController仍然正确设置,所以这很好。 我不确定如果你从一个已经出现的模态控制器中提出你的控制器会发生什么,所以对于更复杂的情况,你将不得不四处实验。

在iOS 8中,您必须操作由viewForKey:返回的viewForKey:而不是由viewControllerForKey:返回的视图控制器的.view属性。 这个testing文档中没有特别的说明,但是如果你查看UIViewControllerTransitioning.h的源代码,你会在viewControllerForKey:上面看到这个注释。

 // Currently only two keys are defined by the // system - UITransitionContextToViewControllerKey, and // UITransitionContextFromViewControllerKey. // Animators should not directly manipulate a view controller's views and should // use viewForKey: to get views instead. - (UIViewController *)viewControllerForKey:(NSString *)key; 

因此,不要调整toViewController.view框架等,使用[transitionContext viewForKey:UITransitionContextToViewKey]的返回值。

如果您的应用程序需要支持iOS7和/或Xcode 5,那么您可以在UIViewController上使用一个简单的类别方法,如下所示:

 - (UIView *)viewForTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext { #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 if ([transitionContext respondsToSelector:@selector(viewForKey:)]) { NSString *key = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey] == self ? UITransitionContextFromViewKey : UITransitionContextToViewKey; return [transitionContext viewForKey:key]; } else { return self.view; } #else return self.view; #endif } 

然后,像往常一样获取您的toViewControllerfromViewController ,但使用[toViewController viewForTransitionContext:transitionContext]获取视图。

编辑:似乎有一个错误,从viewForKey返回呈现视图控制器的视图为零,从而阻止您进行模式转换,根本上animation呈现视图(如滑动或水平翻转)。 我在rdar:// 17961976( http://openradar.appspot.com/radar?id = 5210815787433984)提交了一个iOS8的bug。 另请参阅http://github.com/bcherry/TransitionBug上的示例项目;

编辑2:感谢graveley的build议,使用UIModalPresentationFullScreen修复了这个问题。 也许这不是一个错误。 苹果可能会打算UIModalPresentationCustom只修改传入模式的视图。 如果要修改传出视图,则需要保证新视图的全屏显示? 无论如何,你应该使用viewForKey和UIModalPresentationFullScreen。

我觉得这个背后的理由应该更好地解释。

视图会消失,因为您将呈现的视图控制器的视图从其原始位置(视图层次结构)中取出,放入您的animation师提供的containerView中,但在animation完成后再也不会将其返回。 所以视图控制器的视图完全从窗口中删除了它的超级视图(containerView)。

在iOS 7中,系统总是返回视图控制器的视图,这些视图在转换完成animation自动完成后,将呈现(呈现和呈现)到原来的位置。 这在iOS 8中不再适用于某些演示文稿样式。

规则非常简单:如果视图控制器的视图将在转换结束时完全隐藏(从视图层次结构中移除),animation师只应该操作呈现视图控制器的视图。 换句话说,这意味着在初始演示animation完成之后,只有呈现的视图控制器的视图将是可见的,而不是呈现视图控制器的视图。 例如,如果您将呈现的视图控制器的视图的不透明度设置为50%,并使用UIModalPresentationFullScreen,则无法在呈现的下方看到呈现视图控制器的视图,但是如果使用UIModalPresentationOverFullscreen,您将(UIPresentationController的shouldRemovePresentersView方法负责指定)。

为什么不允许animation制作者随时操作呈现视图控制器的视图? 首先,如果呈现视图控制器的视图在整个演示生命周期中的animation结束后将保持可见状态,则根本不需要对它进行animation处理 – 它只停留在原来的位置。 其次,如果该视图控制器的所有权被转移到表示控制器,则表示控制器很可能不知道如何在需要时布置视图控制器的视图,例如当方向改变时,但呈现视图控制器的原始所有者。

在iOS 8中,引入了viewForKey:方法来获取animation师操纵的视图。 首先,只要animation师不要触摸视图,就可以遵循上述规则,返回零。 其次,它可能会为animation制作者带来不同的视angular。 想象一下,你正在实现一个类似于表格的演示文稿。 在这种情况下,你会想要在呈现的视图控制器的视图周围添加一些阴影或装饰。 animation师将animation装饰,而呈现的视图控制器的视图将成为装饰的孩子。

viewControllerForKey:不会消失,如果需要直接访问视图控制器,仍然可以使用它,但是animation师不应该对需要animation的视图做任何的假设。

有几件事你可以做,以正确地解决问题时消失呈现视图控制器的视图,当你明确地把它放在animation的容器视图:

  1. 如果不需要animation呈现视图控制器的视图,请使用viewForKey:将视图变为animation,而不是直接伸出来查看控制器的视图。 viewForKey:可能返回零甚至完全不同的意见。

  2. 如果你想animation显示视图控制器的视图,你应该考虑使用UIModalPresentationFullScreen样式或继续使用UIModalPresentationCustom和实现你自己的UIPresentationController的子类与shouldRemovePresentersView返回YES 。 实际上,这个方法的实现是由UIModalPresentationFullScreenUIModalPresentationCustom风格定义的内部表示控制器之间的主要区别,除了后者允许您使用自定义表示控制器。

  3. 在所有其他罕见的情况下,您将不得不返回呈现视图控制器的视图到原来的位置,build议其他答案。

没有将modalPresentationStyle设置为UIModalPresentationCustom为我解决了这个问题。

换句话说,留在默认的UIModalPresentationFullScreen,而不是指定UIModalPresentationCustom固定消失的观点问题。 注意UIViewControllerTransitioningDelegate协议似乎仍然被遵循,即使在默认情况下离开它。 如果我记得正确的话,曾几何时,UIModalPresentationCustom是一个要求。

到目前为止,只有尝试过这种非交互式animation。

我在Lefteris的相关主题中发现了这个非常有用的答案: https : //stackoverflow.com/a/27165723/3709173

把它们加起来:

  1. 将modalPresentationStyle设置为.Custom
  2. 子类UIPresentationController,重写shouldRemovePresentersView(带NO)
  3. 覆盖TransitionDelegate类中的presentationControllerForPresentedViewController并返回您的自定义UIPresentationController

+1在您的自定义过渡中,当解散animation发生时不要添加到视图。

在这里演示:

https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl=0没有任何黑客!; 这就像魔术一样! 🙂

在iOS 8中,您需要创build一个UIPresentationController并在UIViewControllerTransitioningDelegate中实现下面的方法。

 - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source; 

在呈现视图控制器时要求您的委托让自定义表示控制器用于pipe理视图层次结构。

返回值:

用于pipe理模态演示文稿的自定义演示文稿控制器。

讨论:

当您使用UIModalPresentationCustom演示样式呈现视图控制器时,系统调用此方法并要求pipe理您的自定义样式的演示控制器。 如果实现此方法,请使用它创build并返回要用于pipe理演示文稿过程的自定义演示文稿控制器对象。

如果不实现此方法,或者如果此方法的实现返回nil,则系统将使用默认的表示控制器对象。 默认的表示控制器不会将任何视图或内容添加到视图层次结构。

可用性iOS 8.0及更高版本中提供。

欲了解更多信息,请观看WWDC 2014video:

https://developer.apple.com/videos/wwdc/2014/?include=228

还有一个来自WWDC的示例代码,名为“LookInside:演示控制器适应性和自定义animation师对象”,您可以从WWDC 2014示例代码页面下载该代码。

您可能需要稍微更改示例代码。 UIPresentationController init方法更改为:

 initWithPresentedViewController:presented presentingViewController:presenting 

提交之前,然后提交。 只要交换它们,它应该工作。

这是Ash的修复程序的Objective C版本。

 // my attempt at obj-c version of Ash's fix UIView *theToView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view; [[[UIApplication sharedApplication] keyWindow] addSubview:theToView]; [transitionContext completeTransition:YES] 

我必须交换顺序,并在添加视图后调用[transitionContext completeTransition:]方法,以从另一个视图控制器的解除完成块呈现一个新的视图控制器来正常工作。

我不知道这会为每个人解决它,但它在我的应用程序。 干杯!

而不是[inView insertSubview:toViewController.view aboveSubview:fromViewController.view]; 只需添加:[inView addSubview:toViewController.view];

 if (self.presenting) { [transitionContext.containerView addSubview:toViewController.view]; // your code } else { // your code } 

你可以在这里看到一个例子: 链接 ,它适用于iOS 7和iOS 8

我发现这对Obj-C很好:

  [transitionContext completeTransition:YES]; if(![[UIApplication sharedApplication].keyWindow.subviews containsObject:toViewController.view]) { [[UIApplication sharedApplication].keyWindow addSubview:toViewController.view]; } 

似乎在ios7和ios8上都能正常工作。

我发现viewForKey:UITransitionContextToViewKey在ios8上返回nil。 所以,如果它是零,我从“到”视图控制器抓取视图。

但是,当completeTransition:YES被调用时,这似乎导致'to'视图不能从容器移动到窗口。 所以,如果viewForKey:UITransitionContextToViewKey返回nil,我viewForKey:UITransitionContextToViewKey回到toVC.view ,并跟踪它返回nil的事实,完成后我将它移动到容器的最初的超级视图(恰好是窗口)。

所以,这个代码在iOS7和iOS8上都可以工作,即使他们修复也不能在iOS9上工作。

 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { // Get the 'from' and 'to' views/controllers. UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)]; // viewForKey is iOS8+. UIView *fromView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextFromViewKey] : fromVC.view; UIView *toView = hasViewForKey ? [transitionContext viewForKey:UITransitionContextToViewKey] : toVC.view; // iOS8 has a bug where viewForKey:to is nil: http://stackoverflow.com/a/24589312/59198 // The workaround is: A) get the 'toView' from 'toVC'; B) manually add the 'toView' to the container's // superview (eg the root window) after the completeTransition call. BOOL toViewNilBug = !toView; if (!toView) { // Workaround by getting it from the view. toView = toVC.view; } UIView *container = [transitionContext containerView]; UIView *containerSuper = container.superview; // Used for the iOS8 bug workaround. // Perform the transition. toView.frame = container.bounds; [container insertSubview:toView belowSubview:fromView]; [UIView animateWithDuration:kDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ fromView.frame = CGRectOffset(container.bounds, 0, CGRectGetHeight(container.bounds)); } completion:^(BOOL finished) { [transitionContext completeTransition:YES]; if (toViewNilBug) { [containerSuper addSubview:toView]; } }]; } 

我发现如果设置了modalPresentationStyle = UIModalPresentationFullScreen ,这个bug(以及更多!)就消失了。 你当然仍然得到你的自定义过渡animation。

遇到这个问题后,我感到非常困惑,因为不久前我写了几乎相同的东西,工作得很好。 来到这里寻找答案来find修复,看起来很hacky,似乎并不了解根本原因…它实际上很容易修复。

有些答案提到将modalPresentationStyle更改为.overFullScreen 。 这是正确的, .overCurrentContext也可以。 这是预料之中的,苹果文件的行为。 但是为什么这不适合每个人? 为什么所有的hacky代码,和这个与其他的组合,以及你不应该做的疯狂的东西?

事实certificate,您需要设置视图加载之前的演示风格。 不在之后。 在init中执行,或者从前一个控制器执行,或者只要在视图加载之前执行。

使用新的UIModalPresentationOverCurrentContext修复了我。 我在iOS 7上的原始过渡只是模糊背景下的视图背景模糊。

我也被困在这个问题上。 我正在寻找创build一个半透明的背景的自定义过渡,我仍然可以看到我来自的视图控制器,但我只有一个黑色的背景。 我发现在这个线程中Mark Aron的答案对我有帮助,但是它是用Objective C编写的,所以这里是我已经testing过的iOS 9和iOS 10的Swift 3版本:

  1. 创buildUIPresentationController的子类。 将shouldRemovePresentersView覆盖为false,如下所示:

     class ModalPresentationController: UIPresentationController { override var shouldRemovePresentersView: Bool { return false } override func containerViewWillLayoutSubviews() { presentedView?.frame = frameOfPresentedViewInContainerView } } 
  2. 在实例化新视图控制器并设置其转换委托的地方,请指出您希望它显示自定义模态演示样式,如下所示:

     let newVC = mainStoryboard.instantiateViewController(withIdentifier: "newVC") as! NewViewController newVC.transitioningDelegate = self newVC.modalPresentationStyle = UIModalPresentationStyle.custom newVC.modalPresentationCapturesStatusBarAppearance = true //optional present(newVC, animated: true, completion: nil) 
  3. 现在重写你的UIViewControllerTransitioningDelegate的presentationController方法,并返回你自定义的UIPresentationController。 我有我的作为我现在的class级的延伸:

     extension CurrentViewController: UIViewControllerTransitioningDelegate { //this is where you implement animationController(forPresented) and animationController(forDismissed) methods func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return ModalPresentationController(presentedViewController: presented, presenting: source) } } 

另外要注意的是,你不应该在你的presentAnimator类中引用你的fromView。 这将是零,你会在运行时得到一个错误。 除此之外,如果你实现的东西,你会得到你的自定义过渡与animation和半透明的背景,如果你做一个。

添加一个视图控制器作为另一个视图控制器的孩子。

 [self addChildViewController:childViewController]; 

检查并让我知道。