在从一个视图过渡到另一个视图时,我一直在遵循一些教程来创建自定义动画.

我的测试项目使用了here个自定义segue,效果很好,但有人告诉我,不再鼓励在自定义segue中制作自定义动画,我应该使用UIViewControllerAnimatedTransitioning.

我遵循了几个使用此协议的教程,但它们都是关于模态表示的(例如,this tutorial).

我正在try 做的是导航控制器树中的推段,但是当我try 对显示(推送)段做同样的事情时,它不再起作用了.

请告诉我在导航控制器中从一个视图到另一个视图进行自定义过渡动画的正确方法.

有没有什么方法可以让我使用一种方法来处理所有的过渡动画呢?如果有一天我想做同样的动画,但最终不得不复制代码两次才能完成模态与控制器的转换,那就太尴尬了.

推荐答案

要使用导航控制器(UINavigationController)执行自定义过渡,您应该:

  • 定义视图控制器以符合UINavigationControllerDelegate协议.例如,您可以在视图控制器的.m文件中使用私有类扩展来指定与此协议的一致性:

    @interface ViewController () <UINavigationControllerDelegate>
    
    @end
    
  • 确保将视图控制器实际指定为导航控制器的代理:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.navigationController.delegate = self;
    }
    
  • 在视图控制器中实现animationControllerForOperation:

    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                      animationControllerForOperation:(UINavigationControllerOperation)operation
                                                   fromViewController:(UIViewController*)fromVC
                                                     toViewController:(UIViewController*)toVC
    {
        if (operation == UINavigationControllerOperationPush)
            return [[PushAnimator alloc] init];
    
        if (operation == UINavigationControllerOperationPop)
            return [[PopAnimator alloc] init];
    
        return nil;
    }
    
  • 实现推送和弹出动画的动画师,例如:

    @interface PushAnimator : NSObject <UIViewControllerAnimatedTransitioning>
    
    @end
    
    @interface PopAnimator : NSObject <UIViewControllerAnimatedTransitioning>
    
    @end
    
    @implementation PushAnimator
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.5;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
        [[transitionContext containerView] addSubview:toViewController.view];
    
        toViewController.view.alpha = 0.0;
    
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            toViewController.view.alpha = 1.0;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }
    
    @end
    
    @implementation PopAnimator
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.5;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController* toViewController   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
        [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view];
    
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            fromViewController.view.alpha = 0.0;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }
    
    @end
    

    这确实会淡入淡出过渡,但您应该可以根据自己的喜好自由自定义动画.

  • 如果您想要处理交互手势(例如,类似于从左向右滑动以弹出的本机手势),您必须实现一个交互控制器:

    • 定义交互控制器(符合UIViewControllerInteractiveTransitioning的对象)的属性:

      @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactionController;
      

      这个UIPercentDrivenInteractiveTransition是一个很好的对象,它可以根据手势的完成程度来完成更新自定义动画的繁重任务.

    • 将手势识别器添加到您的视图中.在这里,我只是实现左侧手势识别器来模拟弹出:

      UIScreenEdgePanGestureRecognizer *edge = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFromLeftEdge:)];
      edge.edges = UIRectEdgeLeft;
      [view addGestureRecognizer:edge];
      
    • 实现手势识别器处理程序:

      /** Handle swipe from left edge
       *
       * This is the "action" selector that is called when a left screen edge gesture recognizer starts.
       *
       * This will instantiate a UIPercentDrivenInteractiveTransition when the gesture starts,
       * update it as the gesture is "changed", and will finish and release it when the gesture
       * ends.
       *
       * @param   gesture       The screen edge pan gesture recognizer.
       */
      
      - (void)handleSwipeFromLeftEdge:(UIScreenEdgePanGestureRecognizer *)gesture {
          CGPoint translate = [gesture translationInView:gesture.view];
          CGFloat percent   = translate.x / gesture.view.bounds.size.width;
      
          if (gesture.state == UIGestureRecognizerStateBegan) {
              self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
              [self popViewControllerAnimated:TRUE];
          } else if (gesture.state == UIGestureRecognizerStateChanged) {
              [self.interactionController updateInteractiveTransition:percent];
          } else if (gesture.state == UIGestureRecognizerStateEnded) {
              CGPoint velocity = [gesture velocityInView:gesture.view];
              if (percent > 0.5 || velocity.x > 0) {
                  [self.interactionController finishInteractiveTransition];
              } else {
                  [self.interactionController cancelInteractiveTransition];
              }
              self.interactionController = nil;
          }
      }
      
    • 在导航控制器委托中,您还必须实现interactionControllerForAnimationController个委托方法

      - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                               interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
          return self.interactionController;
      }
      

如果你在谷歌上搜索"UINavigationController自定义转换教程",你会得到很多结果.或者看到WWDC 2013 Custom Transitions video.

Ios相关问答推荐

什么是最好的方式使用DateFormatters在可扩展视图cellForRowAt方法

为什么导航栏在与Style.Page的选项卡栏一起使用时停止工作?

IFrame内容拒绝仅在iOS浏览器上加载-即使发布了内容-安全-策略(CSP)框架-祖先指令

ScrollViewReader Scrollto Not Working

带菜单的选取器不适用于Int32,但适用于Int

Xcode 15阻止我的应用程序运行:由于实时模式录制的高比率,丢失了1条日志(log)/路标消息?

如果使用 .onAppear 设置,为什么 SwiftUI Picker 不显示正确的选定值

ionic 4 - 编译 ios - 来自项目Pods的目标GoogleDataTransport中的问题

通过 Rosetta 打开模拟器 xcode 14 以修复滚动

在 SwiftUI 中打开 PDF

SensorKit - 获取数据不调用结果委托函数

缺少推送通知权利

在 iOS 编程中使用 Storyboard 代替 xib 文件有什么好处?

UIView 动画与 CALayers

如何缩小 UIImage 并使其同时变得清晰/锐利而不是模糊?

如果已安装,则重定向到应用程序,否则重定向到 App Store

如何识别导航堆栈中的前一个视图控制器

使用 swift 将本地 html 加载到 UIWebView

快速从 NSTimeInterval 转换为小时、分钟、秒、毫秒

从 Swift 转换为 Objective-c 的工具