iOS 7 仅在某些时候使用自定义交互过渡

Posted

技术标签:

【中文标题】iOS 7 仅在某些时候使用自定义交互过渡【英文标题】:iOS 7 use custom interactive transitions only some of the time 【发布时间】:2013-11-21 06:07:33 【问题描述】:

所以我有一个 UINavigationController,控制器 A 作为根控制器。

当我想将控制器 B 推到顶部时,我想使用自定义动画过渡和自定义交互过渡。这很好用。

当我想将控制器 C 推到顶部时,我想退回到 UINavigationController 附带的 默认 推送/弹出转换。为了实现这一点,我为

返回 nil
navigationController:animationControllerForOperation:fromViewController:toViewController:

但是如果你返回 nil,那么

navigationController:interactionControllerForAnimationController:

永远不会被调用,默认的“从左边缘平移”弹出交互过渡不起作用。

有没有办法返回默认的推送/弹出动画控制器和交互控制器? (id<UIViewControllerAnimatedTransitioning>id<UIViewControllerInteractiveTransitioning>有具体实现吗?)

还是其他方式?

【问题讨论】:

【参考方案1】:

您应该将 NavigationController 的 interactivePopGestureRecognizer 委托设置为 self,然后在 -gestureRecognizerShouldBegin 中处理其行为:

也就是说,当您想要触发内置弹出手势时,您必须从此方法返回 YES。您的自定义手势也是如此 - 您必须弄清楚您正在处理的识别器。

- (void)setup

    self.interactiveGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTransitionGesture:)];
    self.interactiveGestureRecognizer.delegate = self;
    [self.navigationController.view addGestureRecognizer:self.interactiveGestureRecognizer];

    self.navigationController.interactivePopGestureRecognizer.delegate = self;


- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

    // Don't handle gestures if navigation controller is still animating a transition
    if ([self.navigationController.transitionCoordinator isAnimated])
        return NO;

    if (self.navigationController.viewControllers.count < 2)
        return NO;

    UIViewController *fromVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count-1];
    UIViewController *toVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count-2];

    if ([fromVC isKindOfClass:[ViewControllerB class]] && [toVC isKindOfClass:[ViewControllerA class]])
    
        if (gestureRecognizer == self.interactiveGestureRecognizer)
            return YES;
    
    else if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer)
    
        return YES;
    

    return NO;

您可以查看sample project 以了解您的情况。视图控制器 A 和 B 之间的转换是自定义动画,带有自定义 B->A 弹出手势。视图控制器 B 和 C 之间的转换是默认的,带有内置导航控制器的弹出手势。

希望这会有所帮助!

【讨论】:

根据 Apple 的 DTS 人员的说法,UINavigationController 附带的私有 interactivePopGestureRecognizer 在其委托中进行了许多检查,其中一些调用了私有 API。实现自己的委托并不能保证 100% 覆盖所有情况。 @Ziconic 案例?【参考方案2】:

您需要在每次演示之前设置委托 - 例如在 prepareForSeque: 中。如果您想要自定义过渡,请将其设置为 self.如果你想要默认转换(如默认弹出转换),请将其设置为 nil。

【讨论】:

遗憾的是,这实际上是 Apple DTS 人员推荐的正确答案和方法,尽管它不是一个特别优雅的方法。基本上,您需要有 2 个 UINavigationControllerDelegate 对象,一个实现转换回调,一个不实现(或为零)。要获得默认的弹出转换,请使用不带回调的委托(或将委托设置为 nil)。要获得自定义转换,请将委托换成具有回调的委托(并在转换完成后恢复原始委托)。 这仍然是推荐的方法吗?【参考方案3】:

在每次转换之前/之后设置委托是一种有效的解决方法,但是如果您实现了其他 UINavigationControllerDelegate 的方法并且您需要保留它们,您可以按照 Ziconic 的建议拥有 2 个委托对象或使用 NSObject 的 @ 987654322@。在您的导航委托中,您可以实现:

- (BOOL)respondsToSelector:(SEL)aSelector

    if (aSelector == @selector(navigationController:animationControllerForOperation:fromViewController:toViewController:) ||
        aSelector == @selector(navigationController:interactionControllerForAnimationController:)) 

        return self.interactivePushTransitionEnabled;
    

    return [super respondsToSelector:aSelector];

然后,您应该确保根据需要更新 interactivePushTransitionEnabled。在您的示例中,您应该仅在显示控制器 A 时将属性设置为 YES

只有一件事要做:强制 UINavigationController 重新评估其委托实现的方法。这可以通过执行以下操作轻松完成:

navigationController.delegate = nil;
navigationController.delegate = self; // or whatever object you use as the delegate

【讨论】:

【参考方案4】:

在您完成自定义转换后,尝试将 (self.)navigationController.delegate 设置为 nil(或将其恢复为以前的值)。

【讨论】:

【参考方案5】:

在您的手势识别器中,将委托(对您自己)设置在那里并返回您的动画和交互式过渡。返回交互式过渡时,请务必再次取消设置委托,以便其他所有内容继续使用默认过渡。

我有一个working example on github。

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController

  // Unset the delegate so that all other types of transitions (back, normal animated push not initiated by a gesture) get the default behavior.
  self.navigationController.delegate = nil;

  if (self.edgePanGestureRecognizer.state == UIGestureRecognizerStateBegan) 
    self.percentDrivenInteractiveTransition = [UIPercentDrivenInteractiveTransition new];
   else 
    self.percentDrivenInteractiveTransition = nil;
  

  return self.percentDrivenInteractiveTransition;

【讨论】:

以上是关于iOS 7 仅在某些时候使用自定义交互过渡的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 自定义转换故障

使用带有子类 UINavigationController 的自定义 iOS 7 过渡偶尔会导致黑屏

自定义交互过渡动画

iOS - 创建自定义过渡动画

带有 UINavigationController 的 iOS 7 自定义当前转换

iOS:自定义后退按钮过渡动画