关于 iOS自定义转场动画

    xiaoxiao2026-05-26  5

    自定义转场动画,首先得确定要做什么样的动画

    1.动画类

    继承自NSObject,服从协议UIViewControllerAnimatedTransitioning

    既然服从这个协议,那么这个协议里的方法就要实现

    这个是用来确定动画时间的,可以在第二个协议方法中用到动画时间的时候用self来调用获取

    // This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to

    // synchronize with the main animation. 

    - (NSTimeInterval)transitionDuration:(nullableid <UIViewControllerContextTransitioning>)transitionContext;

    这个是用来确定做什么样的转场动画

    // This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.

    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

    2.交互类

    对于动画类方面push和present转场其实都是一样的

    但是对于交互类,这两者可不太相同

    先说说push,pop

    继承自NSObject,要服从协议UINavigationControllerDelegate

    在交互类的.m文件中定义两个私有属性,一个UINavigationController属性,一个UIPercentDrivenInteractiveTransition属性,前者是该交互类对应的导航栏,后者是用来实现根据手势的完成比例更新UI的

    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationControlle *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {

        /**

         *  方法1中判断如果当前执行的是Pop操作,就返回我们自定义的Pop动画对象。

         */

        if (operation ==UINavigationControllerOperationPop)

            return [[PopAnimationalloc]init];

        returnnil;

    }

    - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController

                             interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {

        /**

         *  方法2会传给你当前的动画对象animationController,判断如果是我们自定义的Pop动画对象,那么就返回interactivePopTransition来监控动画完成度。

         */

        if ([animationControllerisKindOfClass:[PopAnimationclass]])

            returnself.interactivePopTransition;

        returnnil;

    }

    这两个是导航栏协议方法:第一个方法根据 operation的枚举值来确定返回push动画类,还是pop动画类

    第二个方法是根据animationController这个动画类的类型来返回服从了交互协议UIViewControllerInteractiveTransitioning的实例,而交互类的私有属性UIPercentDrivenInteractiveTransition正好就是一个服从该协议的类

    除了协议之外,交互类还要实现pan手势触发的方法,在这个方法中做动画的更新UI的操作

    /**

     *  我们把用户的每次Pan手势操作作为一次pop动画的执行

     */

    - (void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {

        /**

         *  interactivePopTransition就是我们说的方法2返回的对象,我们需要更新它的进度来控制Pop动画的流程,我们用手指在视图中的位置与视图宽度比例作为它的进度。

         */

        CGFloat progress = [recognizertranslationInView:recognizer.view].x / recognizer.view.bounds.size.width;

        /**

         *  稳定进度区间,让它在0.0(未完成)~1.0(已完成)之间

         */

        progress = MIN(1.0,MAX(0.0, progress));

        if (recognizer.state ==UIGestureRecognizerStateBegan) {

            /**

             *  手势开始,新建一个监控对象

             */

            self.interactivePopTransition = [[UIPercentDrivenInteractiveTransitionalloc]init];

            /**

             *  告诉控制器开始执行pop的动画

             */

            [self.vcpopViewControllerAnimated:YES];

        }

        elseif (recognizer.state ==UIGestureRecognizerStateChanged) {

            /**

             *  更新手势的完成进度

             */

            [self.interactivePopTransitionupdateInteractiveTransition:progress];

        }

        elseif (recognizer.state ==UIGestureRecognizerStateEnded || recognizer.state ==UIGestureRecognizerStateCancelled) {

            /**

             *  手势结束时如果进度大于一半,那么就完成pop操作,否则重新来过。

             */

            if (progress >0.5) {

                [self.interactivePopTransitionfinishInteractiveTransition];

            }

            else {

                [self.interactivePopTransitioncancelInteractiveTransition];

            }

           self.interactivePopTransition =nil;

        }

    }

    再就是初始化方法 需要注意的是,在此处将对应的导航栏的代理设置成本交互类

    - (instancetype)initWithViewController:(UIViewController *)vc

    {

        self = [superinit];

        if (self) {

            self.vc = (UINavigationController *)vc;

            self.vc.delegate =self;

        }

        returnself;

    }

    再说说如何使用

    创建一个导航栏的子类

     UIGestureRecognizer *gesture =self.interactivePopGestureRecognizer;

        gesture.enabled =NO;

        UIView *gestureView = gesture.view;

        

        UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizeralloc]init];

        popRecognizer.delegate =self;

        popRecognizer.maximumNumberOfTouches =1;

        [gestureView addGestureRecognizer:popRecognizer];

      

        _navT = [[NavigationInteractiveTransitionalloc]initWithViewController:self];

        [popRecognizer addTarget:_navTaction:@selector(handleControllerPop:)];

    这个是需要注意的地方

    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

        /**

         *  这里有两个条件不允许手势执行,1、当前控制器为根控制器;2、如果这个pushpop动画正在执行(私有属性)

         */

        returnself.viewControllers.count !=1 && ![[selfvalueForKey:@"_isTransitioning"]boolValue];

    }

    说完了push,然后说说present,dismiss

    动画类类似

    说交互类

    继承自NSObject,要服从协议 UIViewControllerTransitioningDelegate

    (这里要说一下的是,交互类实例创建后,要把被模态出来的控制器赋值给交互类的属性)

    初始化交互类

    被模态出来的控制器的transitioningDelegate设置为该交互类实例

    如果要支持手势滑动返回那么需要再创建一个继承自UIPercentDrivenInteractiveTransition的子类,并且以被模态出来的控制器初始化

    注意手势是添加在被模态出来的控制器的view上的

    转载请注明原文地址: https://ju.6miu.com/read-1310089.html
    最新回复(0)