"从View Controller"使用UIViewControllerContextTransitioning消失

Nir*_*tel 103 iphone transition objective-c uiview ios

我有一个问题,我在下面描述了它.

我正在使用UIViewControllerContextTransitioning进行自定义转换.

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

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

但我无法看到第一个视图控制器,我只能看到第二视图控制器下方的黑屏.

这是代码.

-(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];
    }];
}
Run Code Online (Sandbox Code Playgroud)

UIViewControllerContextTransitioning调用时,突然的第一视图控制器消失,下面第二视图控制器黑色屏幕.

有没有人有想法?谢谢.

Ash*_*row 95

我在这里遇到了同样的问题 - 看起来像iOS 8中的一个错误.我已经提交了一个雷达.

屏幕变黑后,我使用Reveal检查视图层次结构.密钥UIWindow完全是空的 - 根本没有视图层次结构!

Reveal'd

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

transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)
Run Code Online (Sandbox Code Playgroud)

我已经检查过,关键窗口rootViewController仍然正确设置,所以没关系.我不确定如果你从一个已经呈现的模态控制器中展示你的控制器会发生什么,所以对于更复杂的情况,你将不得不试验一下.

  • 我发现如果你设置`modalPresentationStyle = UIModalPresentationFullScreen`,这个bug(以及更多!)就会消失.您当然仍然可以获得自定义过渡动画. (21认同)
  • 我也看到过这个问题.iOS 8引入了一种新方法和访问fromView和toView的键(注意:不是视图控制器)看起来这些引用在转换过程中不会丢失.您可以像往常一样将它们添加到容器视图中,只需从视图控制器中检索它们即可. (2认同)

egd*_*try 77

我觉得应该更好地解释这背后的原因.

视图消失是因为您从其原始位置(视图层次结构)中取出呈现视图控制器的视图,将其放在动画制作者提供的containerView内,但在动画结束后永远不会将其返回.因此,视图控制器的视图将完全从窗口中删除其superview(containerView).

在iOS 7中,系统总是返回视图控制器的视图,这些视图在转换完成动画自动化后,将其呈现(呈现和呈现)到其原始位置.iOS 8中的某些演示样式不再发生这种情况.

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

为什么不允许动画师始终操纵呈现视图控制器的视图?首先,如果在整个演示生命周期中动画结束后呈现视图控制器的视图将保持可见,则根本不需要对其进行动画处理 - 它只是保持原样.其次,如果该视图控制器的所有权被转移到演示控制器,则演示控制器很可能不知道如何在需要时布置该视图控制器的视图,例如当方向改变时,但是呈现视图控制器的原始所有者.

在iOS 8 viewForKey:中引入了一种方法来获取动画师操作的视图.首先,每当动画师不应该触摸视图时,返回nil有助于遵循上述规则.其次,它可能会为动画制作者提供不同的动画效果.想象一下,您正在实施类似于表单的演示文稿.在这种情况下,您可能希望在呈现的视图控制器视图周围添加一些阴影或装饰.动画师将为该装饰设置动画,并且呈现的视图控制器的视图将是装饰的子视图.

viewControllerForKey: 如果不需要直接访问视图控制器但动画师不应该对动画所需的视图做出任何假设,它仍然可以使用.

当您将其显式放置在动画制作者的容器视图中时,有几件事可以正确解决一个消失的呈现视图控制器视图的问题:

  1. 如果不需要为呈现视图控制器的视图设置动画,则使用viewForKey:获取动画视图而不是直接查看控制器视图.viewForKey:可能会返回零或甚至完全不同的观点.

  2. 如果要为呈现视图控制器的视图设置动画,则应考虑使用UIModalPresentationFullScreen样式或继续使用UIModalPresentationCustom并实现自己的UIPresentationController子类并shouldRemovePresentersView返回YES.事实上,这种方法的实现是内部表示控制器之间的主要区别,UIModalPresentationFullScreen并且UIModalPresentationCustom除了后者允许您使用自定义表示控制器之外的样式.

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

  • OMG,这需要更多的投票.解决了我的问题100% (6认同)
  • 这是非常奇怪的,因为[此代码](https://github.com/artsy/eidolon/blob/master/Kiosk/Help/HelpAnimator.swift#L19-L20)依赖于`viewControllerForKey:`的`view`s只有当`viewForKey:`返回nil时,我*仍然*必须手动将它重新添加到窗口中.您是否有一个没有此解决方法的代码工作示例? (2认同)

bch*_*rry 70

在iOS 8中,您必须操纵返回的视图,viewForKey:而不是操作返回.view的视图控制器的属性viewControllerForKey:.这在beta文档中并不是特别清楚,但是如果你查看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;
Run Code Online (Sandbox Code Playgroud)

因此,不要调整帧等,而是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
}
Run Code Online (Sandbox Code Playgroud)

然后,让你toViewControllerfromViewController往常一样,但使用得到的意见[toViewController viewForTransitionContext:transitionContext].

编辑:似乎存在一个错误,其中呈现视图控制器的视图在返回时为零viewForKey,这会阻止您进行模拟过渡,使所有呈现视图动画化(例如滑动或水平翻转).我在rdar:// 17961976(http://openradar.appspot.com/radar?id=5210815787433984)上为iOS8提交了一个错误.另请参阅http://github.com/bcherry/TransitionBug上的示例项目

编辑2:感谢graveley的建议,使用UIModalPresentationFullScreen修复了这个问题.也许这不是一个错误.Apple可能打算让UIModalPresentationCustom仅修改传入模式的视图.如果要修改外发视图,是否需要保证全屏显示新视图?无论如何,您应该使用viewForKey和UIModalPresentationFullScreen.

  • @kenKuan你可能是对的.当使用UIModalPresentationFullScreen时,`viewForKey`会返回视图和视图.所以也许它故意为UIModalPresentationCustom返回nil.我正在更新我的错误报告,如果我从Apple那里收到回复,我会回复此处. (4认同)
  • viewForKey错误让我发疯了! - 感谢提交.通过从UITransitionContextToViewControllerKey获取视图,我的转换工作正常,但我的转换仅将转换应用于整个视图.我不确定这是否应该被解释为"操纵"VC的观点或不... (2认同)
  • 我认为根据` - viewForKey` // viewForKey:**的注释可能会返回nil**,这表示动画师不应该操纵关联的视图控制器的视图.返回"nil"不是错误. (2认同)

gra*_*ley 24

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

换句话说,保留默认的UIModalPresentationFullScreen而不是指定UIModalPresentationCustom修复了消失的视图问题.请注意,即使将此保留为默认值,UIViewControllerTransitioningDelegate协议似乎仍然会被遵循.如果我没记错的话,曾几何时UIModalPresentationCustom是一个要求.

到目前为止的工作,只为非交互式动画尝试过.

  • 虽然这似乎解决了这个问题,但重要的是要注意视图控制器后面的屏幕一旦显示就会变黑.如果您的视图控制器不是全屏,这一点很重要. (4认同)

小智 15

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

把它们加起来:

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

在自定义转换中+1,在解雇动画发生时不要添加到View.

在这里展示:

https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl=0 没有任何黑客攻击!它就像魔术!:)


Pau*_*ria 8

在iOS 8中,您需要在UIViewControllerTransitioningDelegate中创建UIPresentationController并实现下面的方法.

- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source;
Run Code Online (Sandbox Code Playgroud)

要求您的代理人在呈现视图控制器时使用自定义表示控制器来管理视图层次结构.

返回值:

用于管理模态演示的自定义演示控制器.

讨论:

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

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

可用性适用于iOS 8.0及更高版本.

有关更多信息,请观看WWDC 2014视频:

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

WWDC还提供了一个名为"LookInside:Presentation Controllers Adaptivity和Custom Animator Objects"的示例代码,您可以从WWDC 2014示例代码页下载该代码.

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

initWithPresentedViewController:presented presentingViewController:presenting
Run Code Online (Sandbox Code Playgroud)

在它呈现之前然后呈现.只需交换它们就可以了.


小智 7

代替[inView insertSubview:toViewController.view aboveSubview:fromViewController.view]; 只需添加:[inView addSubview:toViewController.view];

if (self.presenting) {

    [transitionContext.containerView addSubview:toViewController.view];
    // your code

} else {
    // your code
}
Run Code Online (Sandbox Code Playgroud)

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


vic*_*on1 7

这是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]
Run Code Online (Sandbox Code Playgroud)

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

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


Ree*_*edy 5

我发现这对Obj-C很好:

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

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


Chr*_*ris 5

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

但是,这似乎导致"to"视图在completeTransition:YES被调用时没有从容器移动到窗口.因此,如果viewForKey:UITransitionContextToViewKey返回nil,我会转向toVC.view,并跟踪它返回nil的事实,并在完成后将其移动到容器的初始superview(恰好是窗口).

所以这段代码适用于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];
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)