iOS 关于自定义转场动画,以UITabBarController为例

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容