美文网首页动画
iOS 关于自定义转场动画,以UITabBarControlle

iOS 关于自定义转场动画,以UITabBarControlle

作者: yep灬 | 来源:发表于2017-05-25 11:36 被阅读64次

    1.小记

    • 关于自定义转场动画,只要你理清他的"套路",你就可以随心所欲地自定义了.
    • 大体思路就是:遵守对应的代理协议,然后设置对应的代理,实现代理方法,这个代理方法要返回的值就是你要实现的动画.(如果返回nil,就是默认效果)
    • 以UITabBarController为例的简单转场动画demo地址 gitHub地址

    2.基本介绍

    在此介绍一下基本知识:

    1.在哪里写我们自定义的动画.

    苹果给我们提供了UIViewControllerAnimatedTransitioning协议,这个协议提供了我们需要的接口,遵守这个协议的对象实现动画基本内容.
    让我们跳转进去看看都有什么:

    @protocol UIViewControllerAnimatedTransitioning <NSObject>
    
    // 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:(nullable id <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;
    
    @optional
    
    /// A conforming object implements this method if the transition it creates can
    /// be interrupted. For example, it could return an instance of a
    /// UIViewPropertyAnimator. It is expected that this method will return the same
    /// instance for the life of a transition.
    - (id <UIViewImplicitlyAnimating>) interruptibleAnimatorForTransition:(id <UIViewControllerContextTransitioning>)transitionContext NS_AVAILABLE_IOS(10_0);
    
    // This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
    - (void)animationEnded:(BOOL) transitionCompleted;
    
    @end
    
    1.通过注释的解释,我们能够知道,遵守UIViewControllerAnimatedTransitioning协议的对象就可以实现我们自定义的动画.
    2.通常我们会自定义NSObject的子类,遵守UIViewControllerAnimatedTransitioning协议,然后实现协议方法来自定义转场动画.
    3.这个子类的对象就是我们的"自定义动画".如果把自定义转场动画比作为做菜的话,那么现在我们准备的就是食材.
    
    • 在这里要对一些概念进行下解释,避免在自定义动画时蒙圈
    1.From和To
    在自定义转场动画的代码中,经常会出现fromViewController和toViewController。如果错误的理解它们的含义会导致动画逻辑完全错误。
    fromViewController表示当前视图容器,toViewController表示要跳转到的视图容器。如果是从A视图控制器present到B,则A是from,B是to。从B视图控制器dismiss到A时,B变成了from,A是to。
    2.Presented和Presenting
    这也是一组相对的概念,它容易与fromView和toView混淆。简单来说,它不受present或dismiss的影响,如果是从A视图控制器present到B,那么A总是B的presentingViewController, B总是A的presentedViewController。
    
    2.在哪里用我们自定义的动画.

    这里要介绍三个协议: 注意每个协议方法的返回值,都是遵守UIViewControllerAnimatedTransitioning的对象

    1.协议一: UIViewControllerTransitioningDelegate
    // 实现present/dismiss动画的接口.
    // 令我们需要自定义动画的控制器遵守UIViewControllerTransitioningDelegate协议,并设置代理,实现协议方法,返回遵守UIViewControllerAnimatedTransitioning协议的类对象即可
    // 在这里需要清楚一点,假设由控制器A present 到B, A遵守UIViewControllerTransitioningDelegate协议,则设置B.transitioningDelegate = A,并设置B.modalPresentationStyle = UIModalPresentationCustom(或UIModalPresentationFullScreen);
    // 一定要设置modalPresentationStyle,不然还是默认的转场动画.
    
    // present动画
    - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
    
    // dismiss动画
    - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
    # modalPresentationStyl
     // 这是一个枚举类型,表示present时动画的类型。
     // 其中可以自定义动画效果的只有两种:FullScreen和Custom,两者的区别在于FullScreen会移除fromView,而Custom不会。
    
    2.协议二:UINavigationControllerDelegate
    // 实现push/pop动画的接口
    // 这里同样是要遵守协议,设置代理,实现协议方法.
    // 注意这里设置的是navigationController.delegate, self.navigationController.delegate = self.
    // 我在其他的博客中看到: (注意: 这里的 self.navigationController.delegate = self 最好写在当前控制器的viewDidAppear方法中, 不然会导致在此push时无动画效果),为什么会失效我还不清楚,希望读者能够找到并分享一下~
    - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                       animationControllerForOperation:(UINavigationControllerOperation)operation
                                                    fromViewController:(UIViewController *)fromVC
                                                      toViewController:(UIViewController *)toVC
    
    
    3.协议三:UITabBarControllerDelegate
    // 实现tabBarController切换子控制器的动画
    // 还是老套路,遵守协议,设置代理,实现协议方法
    // 只是这里要设置tabBarController的代理,我的做法就是在UITabBarController的viewDidLoad方法里设置代理: self.delegate = self;
    - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
                animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);
    
    
    • 小结: 从上面三个协议的返回值能够看出,返回的东西就是我们2.1自定义遵守UIViewControllerAnimatedTransitioning协议的类的对象.

    3.转场动画的思路(纯个人理解,起个抛砖引玉作用~)

    • 步骤一: 明确做哪种转场动画(做哪种菜,是鲁菜,还是川菜?)
    自定义present/dismiss动画要遵守UIViewControllerTransitioningDelegate协议
    自定义push/pop动画要遵守UINavigationControllerDelegate协议
    自定义tabbarController转场动画要遵守UITabBarControllerDelegate协议
    

    以demo为例:

    // DMMainViewController.m文件
    @interface DMMainViewController ()<UITabBarControllerDelegate>// 遵守协议
    @end
    @implementation DMMainViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.delegate = self;// 设置代理
        UIViewController *vc = [[UIViewController alloc] init];
        vc.view.backgroundColor = [UIColor whiteColor];
        [self setChildchildViewController:vc index:0 title:@"我是A"];
        [self setChildchildViewController:[[UITableViewController alloc] init] index:1 title:@"我是B"];
        [self setChildchildViewController:[[UIViewController alloc] init] index:2 title:@"我是C"];
    }
    // 动画 实现协议方法
    - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
        return [[AnimationManager alloc] init];
    }
    
    

    这里其实就是遵守协议,设置代理,实现协议方法.

    • 步骤二: 确定做哪样转场动画(如果选择了鲁菜,是德州扒鸡,还是红烧大虾?如果选择了川菜,是四川火锅,还是水煮鱼?)
    // AnimationManager.h文件
    // 自定义NSObject的子类,遵守UIViewControllerAnimatedTransitioning协议
    @interface AnimationManager : NSObject<UIViewControllerAnimatedTransitioning>
    
    @property (nonatomic, assign) KAnimationType type;
    - (instancetype)initWithType:(KAnimationType)type;
    
    @end
    
    // AnimationManager.m文件
    #import "AnimationManager.h"
    #import "DMNavigationViewController.h"
    
    @interface AnimationManager ()
    @end
    
    @implementation AnimationManager
    
    // 这个是动画时长
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
        
        return 0.5;
    }
    
    // 具体动画,在这里可以根据你的想象去实现你要的动画效果了
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
        
        // 获取fromVc和toVc
        
        DMNavigationViewController *fromVc = (DMNavigationViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        
        DMNavigationViewController *toVc = (DMNavigationViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        
        UIView *fromV = fromVc.view;
        UIView *toV = toVc.view;
        
        
        // 转场环境
        UIView *containView = [transitionContext containerView];
        containView.backgroundColor = [UIColor whiteColor];
        
        
        // 判断滑动方向
        if (toVc.index > fromVc.index) {
        
            toV.frame = CGRectMake([UIScreen mainScreen].bounds.size.width, 0, containView.frame.size.width, containView.frame.size.height);
            
            [containView addSubview:toV];
            // 动画
            [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
                
                fromV.transform = CGAffineTransformTranslate(fromV.transform, -[UIScreen mainScreen].bounds.size.width,0);// containView.frame.size.height
                toV.transform = CGAffineTransformTranslate(toV.transform, -[UIScreen mainScreen].bounds.size.width, 0);
                
            } completion:^(BOOL finished) {
                
                [transitionContext completeTransition:YES];
            }];
            
            
        }else if (toVc.index < fromVc.index) {
            
            toV.frame = CGRectMake(- [UIScreen mainScreen].bounds.size.width, 0, containView.frame.size.width, containView.frame.size.height);
            
            [containView addSubview:toV];
            
            [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
                
                fromV.transform = CGAffineTransformTranslate(fromV.transform, [UIScreen mainScreen].bounds.size.width,0);
                toV.transform = CGAffineTransformTranslate(toV.transform, [UIScreen mainScreen].bounds.size.width, 0);
                
            } completion:^(BOOL finished) {
                
                [fromV removeFromSuperview];
                [transitionContext completeTransition:YES];
    
    
            }];
            
        }
    }
    @end
    
    # 这里面就涉及到前面讲的 1.From和To的关系,2.Presented和Presenting的关系.在2.1的底部有介绍
    

    小结:

    所谓的自定义转场动画,就是把系统默认的换成我们自己写的而已,关键就是在这些协议里.理清控制器与协议的关系.

    简单的画了一个结构图

    架构图

    附: 以UITabBarController为例的简单转场动画demo地址 gitHub地址

    参考文章:iOS自定义转场动画, iOS中应该知道的自定义各种Controller的转场过渡动画

    相关文章

      网友评论

        本文标题:iOS 关于自定义转场动画,以UITabBarControlle

        本文链接:https://www.haomeiwen.com/subject/qykgxxtx.html