iOS开发 tabbar自定义转场动画

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的转场过渡动画

猜你喜欢

转载自blog.csdn.net/ljc_563812704/article/details/81482206