登录 立即注册
金钱:

Code4App-iOS开发-iOS 开源代码库-iOS代码实例搜索-iOS特效示例-iOS代码例子下载-Code4App.com

iOS之实现自定义转场动画

[复制链接]
来自: 李世铿 分类: iOS精品源码 上传时间: 2016-4-18 18:13:34
Tag:控制器 动画 iOS 转场动画 animation animation

项目介绍:

文:chongzone (简书作者)


iOS之实现自定义转场动画
转场/控制器切换
含义:在 NavigationController 里push 或 pop 一个 View Controller,在TabBarController中切换到其他 View Controller,以 Modal方式显示另外一个View Controller,这些都是控制器切换(View Controller Transition),简称转场

在 iOS 7 之前,我们只能使用系统提供的转场效果,iOS 7之后苹果开放了相关 API 允许我们对转场效果进行全面定制,它是 以协议的方式开放了自定义转场的 API,协议的好处是不再拘泥于具体的某个类,只要是遵守该协议的对象都能参与转场,非常灵活,这样对自定义转场动画以及交互手段的支持带来了无限可能。

转场实现的本质
转场的本质是当前视图消失和下一视图出现,基于此进行动画,因为在转场的过程中,作为容器的父控制器维护着多个子控制器,但在视图结构上,只保留一个子控制器的视图。
目前主流的自定义转场
  • 在UINavigationController中 Push 和 Pop
  • 在 UITabBarController中切换 TabBar
  • 模态(Modal) 转场:Present 和 Dismiss,仅限于modalPresentationStyle属性为 UIModalPresentationFullScreen或UIModalPresentationCustom这两种模式
  • UICollectionViewController 布局转场(与 UINavigationController结合的转场方式)


转场动画的实现
转场协议由5种协议组成,在实际开发中只需我们提供其中的两个或三个便能实现绝大部分的转场动画。
  • 1.转场代理(Transition Delegate) - 必须使用:
    自定义转场的第一步便是提供转场代理,告诉系统使用我们提供的代理而不是系统的默认代理来执行转场,不同类型的控制器遵守的协议不同,如下:
[Objective-C] 查看源文件 复制代码
/**
 *  除了<UIViewControllerTransitioningDelegate>是 iOS7 新增的协议,其他两种在 iOS2 里就存在了
 */
<UINavigationControllerDelegate> //UINavigationController 的 delegate 属性遵守该协议
<UITabBarControllerDelegate> //UITabBarController 的 delegate 属性遵守该协议
<UIViewControllerTransitioningDelegate> //UIViewController 的 transitioningDelegate 属性遵守该协议

  • 2.动画控制器(Animation Controller) - 必须使用:
    最重要的部分,遵守<UIViewControllerAnimatedTransitioning>协议,负责添加视图以及执行动画,由我们实现


  • 3.交互控制器(Interaction Controller) - 可选使用:
    通过交互手段,通常是手势来驱动动画控制器实现的动画,使得用户能够控制整个过程;遵守<UIViewControllerInteractiveTransitioning>协议


  • 4.转场环境(Transition Context) - 必须使用:
    提供转场中需要的数据;遵守<UIViewControllerContextTransitioning>协议;由UIKit在转场开始前生成并提供给我们提交的动画控制器和交互控制器使用


  • 5.转场协调器(Transition Coordinator) - 可选使用:
    可在转场动画发生的同时并行执行其他的动画,主要在 Modal 转场和交互转场取消时使用,其他时候很少用到,由 UIKit 在转场时生成,遵守<UIViewControllerTransitionCoordinator>协议;UIViewController 在 iOS 7 中新增了方法transitionCoordinator()返回一个遵守该协议的对象,且该方法只在该控制器处于转场过程中才返回一个此类对象,不参与转场时返回 nil


下面就拿一个我之前写好的小栗子来看看自定义转场是如何实现的吧,这里我以特殊的Modal转场为例讲一下,TabBarController或是NavigationController自定义转场实现相对简单些,但本质其实都是相同的,先看效果图吧:
1.gif
Modal转场

Modal转场的实现
  • Demo内的视图结构
1.png
视图结构.png

首先控制器遵守转场代理
//实现转场的触发操作
[Objective-C] 查看源文件 复制代码
//实现转场的触发操作
- (IBAction)clickBtn {
    PresentedViewController *presentedVC = [PresentedViewController new];

    //系统转场
    //presentedVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical; //默认样式

    //自定义转场 模态转场 需要代理实现
    presentedVC.modalPresentationStyle = UIModalPresentationCustom;

    //遵守转场代理  代理需要强引用(即:self.delegate)
    presentedVC.transitioningDelegate = self.delegate;

    [self presentViewController:presentedVC animated:YES completion:nil];
}


在TransitionDelegate遵守对应的协议
[Objective-C] 查看源文件 复制代码
@interface TransitionDelegate : NSObject<UIViewControllerTransitioningDelegate>


实现其代理协议
[Objective-C] 查看源文件 复制代码
/**
 *  返回当view显示时执行动画的对象,该对象需实现转场动画
 */
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    //return nil;

    return [ModalAnimation new];
}

/**
 *  返回当view消失时执行动画的对象,该对象需实现转场动画
 */
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
    //return nil;

    return [ModalAnimation new];
}




创建动画控制器添加转场视图以及执行相应的动画,对于动画控制器来说,转场方式并不重要,可以对fromView 和 toView 进行任何动画,需要遵守转场动画协议<UIViewControllerAnimatedTransitioning>,并实现对应的协议方法,如下:
[Objective-C] 查看源文件 复制代码
//返回转场动画时间
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
    return 0.5;
}

/**
 *  最重要的方法 必须实现 否则自定义转场无效
 *  该方法接受一个遵守<UIViewControllerContextTransitioning>协议的转场环境对象
 *  该转场环境对象提供了转场所需要的重要数据:参与转场的视图控制器和转场过程的状态信息
 *  在转场开始前<UIKit>生成遵守转场环境协议<UIViewControllerContextTransitioning>的对象transitionContext
 *  转场环境对象transitionContext提供了以下信息
 *  1. containerView 容器视图
 *  2. 获取参与转场的视图控制器,有 UITransitionContextFromViewControllerKey 和 UITransitionContextToViewControllerKey 两个 Key
 *  2. iOS8之后 新增的 API 用于方便获取参与转场的视图,有 UITransitionContextFromViewKey 和 UITransitionContextToViewKey 两个 Key
 *  在 iOS8 中可通过方法"viewForKey"来获取参与转场的三个重要视图,在 iOS7 中则需要通过对应的视图控制器来获取
 */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{

    //获取转场相关的两个控制器 iOS7的API
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    //iOS8之后使用
    //UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    //UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];

    //获取参与转场的视图
    UIView *fromView = fromVC.view;
    UIView *toView = toVC.view;

    //获取容器视图
    UIView *containerView = [transitionContext containerView];

    if (toVC.isBeingPresented) {
        //添加目标View
        [containerView addSubview:toView];

        //实现动画
        toView.transform = CGAffineTransformMakeRotation(-M_PI_2); //设置初始值

        //获取动画的时间
        //NSTimeInterval duration = [self transitionDuration:transitionContext];

        //回到默认位置
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            toView.transform = CGAffineTransformIdentity;
        } completion:^(BOOL finished) {

            /**
             *  正确地结束转场过程。转场的结果有两种:完成或取消
             *  转场动画结束后 必须调用 否则呈现的新视图无法监听任何事件
             *  非交互转场的结果只有完成一种情况,不过交互式转场需要考虑取消的情况
             *  如何结束取决于转场的进度,通过transitionWasCancelled()方法来获取转场的状态,使用completeTransition:来完成或取消转场
             */
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }else{
        //视图消失 Dismiss 转场中不要将 toView 添加到 containerView
        if (fromVC.isBeingDismissed) {
            [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
                fromView.transform = fromView.transform.b > 0.20 ? CGAffineTransformMakeRotation(M_PI_2):CGAffineTransformMakeRotation(-M_PI_2);
            } completion:^(BOOL finished) {
                [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
            }];
        }
    }
}



下面需要处理Modal转场后的控制器,因为Demo实现的一个视图基于某个点旋转,所以这里使用了layer的属性锚点anchorPoint,对于橘色视图的旋转监听,这里采用了拖动手势UIPanGestureRecognizer,代码如下:
[Objective-C] 查看源文件 复制代码
- (void)viewDidLoad {
    [super viewDidLoad];

    /**
     *  自定义转场 这里需要设置控制器View的大小
     *  anchorPoint 锚点 默认是其中心点(0.5,0.5) 设置必须在frame之前
     */
    self.view.backgroundColor = [UIColor orangeColor];
    self.view.layer.anchorPoint = CGPointMake(0.5, 2.0);
    self.view.frame = [UIScreen mainScreen].bounds;

    //创建拖动手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panView:)];

    [self.view addGestureRecognizer:pan];
}



处理拖动手势事件
[Objective-C] 查看源文件 复制代码
- (void)panView:(UIPanGestureRecognizer *)pan{
    switch (pan.state) {
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:{

            NSLog(@"%f...",self.view.transform.b);

            /**
             *  根据矩阵中b的值大小操作橘色View  大于0.20实现让其掉落下来的效果 弧度为正代表顺时针
             *  CGAffineTransform的各种操作 旋转 平移 缩放 本质都是矩阵形式实现
             */
            if (ABS(self.view.transform.b) > 0.20) {
                [UIView animateWithDuration:1.0 animations:^{
                    self.view.transform =  self.view.transform.b >0 ? CGAffineTransformMakeRotation(M_PI_2): CGAffineTransformMakeRotation(-M_PI_2);
                }];

                [self dismissViewControllerAnimated:YES completion:nil]; //保证视图完全移除
            }else{
                self.view.transform = CGAffineTransformIdentity; //默认位置
            }
        }
            break;
        default:{
            //获取偏移量
            CGFloat offSetX = [pan translationInView:self.view].x;

            //计算偏移百分比
            CGFloat percentage = offSetX /self.view.bounds.size.width;

            //计算旋转的度数 这里设置旋转的度数范围 M_PI_2
            CGFloat radios = percentage * M_PI_2;

            //实现旋转
            self.view.transform = CGAffineTransformMakeRotation(radios);
        }
            break;
    }
}




到这里的话,Modal转场的核心基本知识就说完了,嗯…上述步骤不错的话,就能实现一个简单的自定义转场动画,其实对于自定义的转场动画,自定义容器的控制器转场应该是复杂度最高的,这里呢,先说下转场动画的简单使用吧,有什么理解错误的地方欢迎指正,谢谢咯。。。





相关源码推荐:

我来说两句
*滑动验证:
所有评论(105)
只是沉醉 2016-4-18 22:09:40
很给力,Code4App有你更精彩!
回复
只是沉醉 2016-4-18 22:10:37
强烈支持楼主ing……
回复
HWBOBO 2016-4-18 23:39:15
很给力,Code4App有你更精彩!
回复
iupq0650 2016-4-19 09:30:53
虽不明,但觉厉!
回复
KunkkaQi 2016-4-19 12:44:47
精华内容,楼主V5!
回复
KunkkaQi 2016-4-19 13:04:25
虽不明,但觉厉!
回复
青春不朽 2016-4-19 15:39:27
楼主威武啊,Code4App有你更给力!
回复
青春不朽 2016-4-19 15:42:14
楼主用心了,内容非常精彩。
回复
cowboyfzl 2016-4-19 17:00:58
十分感谢,写得好
回复
提取码:  下载次数:105 状态:已购或VIP 售价:0(原价:5)金钱 下载权限:初级码农 
3275 0 105
联系我们
首页/微信公众账号投稿

帖子代码编辑/版权问题

QQ:435399051,742864542

如何获得代码达人称号?

代码贡献英雄榜
用户名 下载数
通过邮件订阅最新 Code4App 信息
上一条 /4 下一条
联系我们
关闭
合作电话:
13802416937
Email:
435399051@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| Github|申请友链|手机版|Code4App ( 粤ICP备15117877号-1 )

快速回复 返回顶部 返回列表