365体育网址View Controller Transition 便是打一个 Scene 转换到另外一个。View Controller Transition 便是起一个 Scene 转换到另外一个。

撤回转场

彼此控制动画时发生或让取消,这频繁带动两个问题:恢复数据以及恶化动画。

这里需要还原的数是selectedIndex,我们当交互转场开始前备份当前的selectedIndex,如果转场取消了不畏运这备份数据恢复。逆转动画反而看起比难以解决。

以上头的 pan
手势处理方法中,我们怎么逆转动画的运行吧?既然speed否0时动画静止不动,调整呢负数是否可以实现逆播放呢?不克,效果是视图消失不见。不过我们还足以调动timeOffset性,从眼前价一直恢复到0。问题是哪发生动画的效能?动画的真面目是看看图属性在某段时间内之总是变,当然者连续变并无是绝对的接连,只要时刻间隔够不够,变化的效果就是会流畅得看上去是连续变,在此处吃这个变化频率和屏幕的刷新同步即可,CADisplayLink得拉我们落实这点,它可在屏幕刷新时之每一样幅执行绑定的计:

//在上面的/*逆转动画*/处添加以下两行代码:
let displayLink = CADisplayLink(target: self, selector: "reverseAnimation:")
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)

func reverseAnimation(displayLink: CADisplayLink){
    //displayLink.duration表示每一帧的持续时间,屏幕的刷新频率为60,duration = 1/60。
    //这行代码计算的是,屏幕刷新一帧后,timeOffset 应该回退一帧的时间。
    let timeOffset = view.layer.timeOffset - displayLink.duration
    if timeOffset > 0{
        containerView.layer.timeOffset = timeOffset
    }else{
        //让 displayLink 失效,停止对当前方法的调用。
        displayLink.invalidate()
        //回到最初的状态。
        containerView.layer.timeOffset = 0
        //speed 恢复为1后,视图立刻跳转到动画的最终状态。
        containerView.layer.speed = 1
    }
}

最后一句代码会使得人疑惑,为何被视图恢复也尾声状态,与我们的初衷南辕北辙。speed不能不恢复为1,不然后续发起的转场动画无法左右逢源执行,视图也无能为力响应触摸事件,直接原因未知。但speed平复为1晚会油然而生一个问题:由于在本来的动画片里
fromView 最终会叫转换出屏幕,尽管 Slide 动画控制器 UIView 动画里的
completion handle 里会回升 fromView 和 toView
的状态,这种状态的面目全非会造成闪屏现象。怎么化解?添加一个借的 fromView 到
containerView替代曾经让转换出屏幕外的实在的
fromView,然后以很不够的工夫间隔后拿的变除了,因为这 fromView
已经归位。在平复speed继补充加以下代码:

let fakeFromView = privateFromViewController.view.snapshotViewAfterScreenUpdates(false)
containerView.addSubview(fakeFromView)
performSelector("removeFakeFromView:", withObject: fakeFromView, afterDelay: 1/60)
//在 Swift 中动态调用私有方法会出现无法识别的选择器错误,解决办法是将私有方法设置为与 objc 兼容,需要添加 @objc 修饰符。
@objc private func removeFakeFromView(fakeView: UIView){
    fakeView.removeFromSuperview()
}

由此考试,上面用来决定与取消 UIView 动画的艺术吧适用于用 Core Animation
实现之卡通片,毕竟 UIView 动画是因此 Core Animation
实现的。不过,我们于头里提到了,官方对 Core Animation
实现的相互转场动画的支撑有弱点,估计官方鼓励下更高级的接口吧,因为转场动画结束晚待调用transitionContext.completeTransition(!isCancelled),而下
Core Animation
完成这无异于步用展开适量的配备,实现的途径来些许种都实现并无略,相比之下
UIView 动画使用 completion block
对斯进行了包装,使用好便利。转场协议的布局都比较复杂了,选择 UIView
动画能够明显下降实现资产。

面的落实忽略了一个细节:时间曲线。逆转动画时每一样轴都回退相同的时光,也就是说,逆转动画的年华曲线是线性的。交互控制器的商<UIViewControllerInteractiveTransitioning>再有零星只可卜方式:

optional func completionCurve() -> UIViewAnimationCurve
optional func completionSpeed() -> CGFloat

就简单只点子记录了动画片采用的动画曲线和进度,在恶化动画时要能基于当时两头计算出当前帧应该回退的时日,那么就会落实完美的恶化,显然这是一个数学问题。恩,我们跨越了这个细节吧,因为自身数学不好,讨论这个题目格外艰难。推荐阅读
Objc.io
的交互式动画同等温情,该文探讨了哪制作自然真实的交互式动画。

进阶

是不是当本文中落实的例证的卡通效果最过简短?的确挺简短,与 VCTransitionsLibrary 这样的转场动画库提供的十种动画效果相比是老大简单的,不过即便动画而言,与本文示例的本来面目是一致的,它们都是本着
fromView 和 toView
的完全进行的动画片,但于效益及越来越扑朔迷离。我当本文中屡屡强调转场动画的本色是凡对将要消失的时视图和即将面世的下一屏幕的情开展动画,「在动画控制器里,参与转场的视图只有
fromView 和 toView
之分,与转场方式无关。转场动画的最终效果无非限制于公的想象力」,当然,还有你的落实力量。

正文前面的目的是赞助你熟悉转场的全部过程,你呢见到了,转场动画里转场部分的贯彻其实十分粗略,大部分犬牙交错的转场动画与本文范例里大概的转场动画相比,复杂的组成部分以动画部分,转场的部分还是一致的。因此,学习了前面的内容后连无可知辅助你顿时就能实现
Github
上那些热门的转场动画,它们变成热点之由来在动画本身,与转场本身关系不大,但她同转场结合后就发出了神奇的力。那学习了当进阶的本章能及时兑现那些热门的转场效果啊?有或,有些力量实在特别简单,一点不怕发,还有部分效应涉及的艺属于本文主题之外的内容,我会见于闹有关的唤起就无深入了。

本章的进阶分为两个部分:

UITabBarControllerDelegate

同一当容器控制器,UITabBarController 的转场代理和
UINavigationController
类似,通过类似之艺术供动画控制器,不过<UINavigationControllerDelegate>的代办方里供了操作类型,但<UITabBarControllerDelegate>的代理方无供滑动的倾向信息,需要我们来获得滑动的动向。

class SDETabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
    //在<UITabBarControllerDelegate>对象里,实现该方法提供动画控制器,返回 nil 则没有动画效果。
    func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController 
                                    fromVC: UIViewController, 
                     toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
        let fromIndex = tabBarController.viewControllers!.indexOf(fromVC)!
        let toIndex = tabBarController.viewControllers!.indexOf(toVC)!

        let tabChangeDirection: TabOperationDirection = toIndex < fromIndex ? .Left : .Right
        let transitionType = SDETransitionType.TabTransition(tabChangeDirection)
        let slideAnimationController = SlideAnimationController(type: transitionType)
        return slideAnimationController
    }
}

也 UITabBarController 设置代理的法门以及陷阱和方的
UINavigationController 类似,注意delegate属性的已故引用问题。点击 TabBar
的附近页面进行切换时,将见面看出 Slide
动画;通过以下代码触发转场时也用张同一的效用:

tabBarVC.selectedIndex = ...//or
tabBarVC.selectedViewController = ...

Demo
地址:ScrollTabBarController。

贯彻分析

既然如此这自定义容器控制器和 UITabBarController
行为看似,我不怕实现了一如既往学类似的 API:viewControllers数组是容器 VC
维护的子 VC 数组,初始化时提供要显得的子
VC,更改selectedIndex的值就可过反至对应的子视图。利用 Swift
的性能观察器实现修改selectedIndex时自动执行子控制器转场。下面是实现子
VC 转场的骨干代码,转场结束后按系统的规矩将 fromView 移除:

class SDEContainerViewController: UIViewController{
    ...
    //发生转场的容器视图,是 root view 的子视图。
    private let privateContainerView = UIView()
    var selectedIndex: Int = NSNotFound{
        willSet{
            transitionViewControllerFromIndex(selectedIndex, toIndex: newValue)
        }
    }
    //实现 selectedVC 转场:
    private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
        //添加 toVC 和 toView
        let newSelectedVC = viewControllers![toIndex]
        self.addChildViewController(newSelectedVC)
        self.privateContainerView.addSubview(newSelectedVC.view)
        newSelectedVC.didMoveToParentViewController(self)

        UIView.animateWithDuration(transitionDuration, animations: {
            /*转场动画*/
            }, completion: { finished in
                //移除 fromVC 和 fromView。
                let priorSelectedVC = viewControllers![fromIndex]
                priorSelectedVC.willMoveToParentViewController(nil)
                priorSelectedVC.view.removeFromSuperview()
                priorSelectedVC.removeFromParentViewController()
        })
    }
}

贯彻转场就是如此十几执行代码而已,其他容器 VC
转场过程做了类似的作业。回忆下我们于动画控制器里开的事务,实际上只是上面代码中之一模一样有。转场协议就套
API
将此历程分割为五只零部件,这套复杂的组织带来了但高度自定义之卡通片效果跟交互控制。我们温习下转场协议,来看望哪当既有的转场协议框架下实现从定义容器控制器的转场动画与相控制:

下我们来将方面的十几实行代码(不包实际的动画代码)使用协议封装成本文前半局部里熟悉的样板。

互动控制

相互之间控制器的说道<UIViewControllerInteractiveTransitioning>独自要求实现一个务必的方式:

func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning)

根据文档的讲述,该方法用于配置与启动交互转场。我们前使用的UIPercentDrivenInteractiveTransition好像提供的更新速度的章程才是调用了转场环境目标的有关方。所以,是转场环境目标同交互控制器把水污染活累在干了,我们的兑现还是保障这种关涉好了。正而前说之,「交互手段只是表现形式,本质是让转场进程」,让咱们返回转场环境目标里心想事成对动画进度的决定吧。

怎么控制动画的快慢?这个题材的本来面目是怎么落实对 UIView
的 animateWithDuration:animations:completion:立刻仿佛措施变的卡通片的决定。能够支配为?能。

卡通控制器协议

动画控制器负责上加视图以及实施动画,遵守UIViewControllerAnimatedTransitioning情商,该谋要求落实以下方法:

//执行动画的地方,最核心的方法。
(Required)func animateTransition(_ transitionContext: UIViewControllerContextTransitioning)
//返回动画时间,"return 0.5" 已足够,非常简单,出于篇幅考虑不贴出这个方法的代码实现。
(Required)func transitionDuration(_ transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
//如果实现了,会在转场动画结束后调用,可以执行一些收尾工作。
(Optional)func animationEnded(_ transitionCompleted: Bool)

最好根本之是首先只艺术,该措施接受一个遵照<UIViewControllerContextTransitioning>说道的转场环境目标,上同样节约之
API
解释里提到是协议,它提供了改动场所得之重要性数据:参与转场的视图控制器和转场过程的状态信息。

UIKit
在转场开始前别遵守转场环境协议<UIViewControllerContextTransitioning>的靶子
transitionContext,它发出以下几只方式来供动画控制器需要的信:

//返回容器视图,转场动画发生的地方。
func containerView() -> UIView?
//获取参与转场的视图控制器,有 UITransitionContextFromViewControllerKey 和 UITransitionContextToViewControllerKey 两个 Key。 
func viewControllerForKey(_ key: String) -> UIViewController?
//iOS 8新增 API 用于方便获取参与参与转场的视图,有 UITransitionContextFromViewKey 和 UITransitionContextToViewKey 两个 Key。
func viewForKey(_ key: String) -> UIView? AVAILABLE_IOS(8_0)

通过viewForKey:博之视图是viewControllerForKey:回的控制器的根视图,或者
nil。viewForKey:计返回 nil 只发同样种植情况: UIModalPresentationCustom
模式下的 Modal 转场 ,通过之措施取得 presentingView 时取的用是
nil,在后的 Modal 转场里会详细分解。

前方提到转场的本色是产一个面貌的视图替换当前光景的视图,从即场面过渡下一个观。下面称将要消失的景的视图为
fromView,对应的视图控制器为 fromVC,即将面世的视图为
toView,对应之视图控制器称之为
toVC。几种植转场方式的转场操作都是可逆的,一栽操作里的 fromView 和 toView
在逆向操作里之角色互换成对方,fromVC 和 toVC
也是如此。于动画控制器里,参与转场的视图只有 fromView 和 toView
之分,与转场方式无关。转场动画的末段效果就限制受您的想象力。
立刻也是卡通控制器在包后好叫第三正应用的严重性原由。

于 iOS 8 中而经过以下方式来取与转场的老三个至关重要视图,在 iOS 7
中虽然需经过相应之视图控制器来取得,为免 API
差异导致代码过长,示例代码中一直运用下的视图变量:

let containerView = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)

Modal 转场的出入

Modal 转场中待举行的工作以及少种容器 VC 的转场一样,但于细节上粗出入。

365体育网址 1

UINavigationController 和 UITabBarController 这片只容器 VC
的根视图在屏幕上是不可见的(或者说是透明底),可见的一味是内嵌在及时两边中之子
VC 中之视图,转场是起子 VC 的视图转换到另外一个子 VC
的视图,其根视图并未介入转场;而 Modal 转场,以 presentation 为例,是从
presentingView 转换到 presentedView,根视图 presentingView 也就是是
fromView 参与了转场。而且 NavigationController 和 TabBarController
转场中的 containerView 也绝不就两头的根视图。

Modal 转场与片栽容器 VC 的转场的另外一个差是:Modal 转场结束后
presentingView 可能还是可见,UIModalPresentationPageSheet
模式就是是这般。这种不同造成了 Modal 转场和容器 VC 的转场对 fromView
的处理差异:容器 VC 的转场结束后 fromView
会被主动移出视图结构,这是不过预见的结果,我们为可于转场结束前手动移除;而
Modal 转场中,presentation 结束后 presentingView(fromView)
并未主动给由视图结构中移除。准确的话,是 UIModalPresentationCustom
这种模式下之 Modal 转场结束时 fromView
并未从视图结构中移除;UIModalPresentationFullScreen 模式之 Modal
转场结束晚 fromView 依然主动给由视图结构被改换除了。这种差异导致在处理
dismissal
转场的下很爱出现问题,没有意识及是不同点的说话有错时便会见毫不头绪。下面来看看
dismissal 转场时之面貌。

ContainerView 于转场期间作为 fromView 和 toView
的父视图。三栽转场过程被之 containerView 是 UIView
的私房子类,不过我们并不需要关心 containerView 具体是啊。在 dismissal
转场中:

  1. UIModalPresentationFullScreen 模式:presentation 后,presentingView
    被主动移出视图结构,在 dismissal 中 presentingView 是 toView
    的角色,其以见面重新加入 containerView
    中,实际上,我们无主动以其投入,UIKit
    也会见这样做,前面的片种容器控制器的转场里不是这么处理的,不过此出入基本没什么影响。
  2. UIModalPresentationCustom 模式:转场时 containerView 并无做
    presentingView 的父视图,后者由 UIKit 另行管理。在 presentation
    后,fromView(presentingView) 未受转移有视图结构,在 dismissal
    中,注意不要像任何转场中那么以 toView(presentingView) 加入
    containerView 中,否则本来可见的 presentingView
    将会见叫移除出我所处的视图结构没有不见。如果您以用 Custom
    模式时无注意到当时点,就充分易丢失进之陷阱而不行为难发现问题所在,这个题材既烦了自我同上。

对 Custom 模式,我们得参照其他转场里的拍卖规则来打理:presentation
转场结束晚积极以 fromView(presentingView)
移有它的视图结构,并因而一个变量来保障 presentingView 的父视图,以便在
dismissal 转场中平复;在 dismissal 转场中,presentingView 的角色由原的
fromView 切换成了
toView,我们再度将那还恢复它原本的视图结构中。测试表明这样做是有效的。但是这样一来,在实现达标,需要以转场代理中保护一个卡通控制器又这动画控制器要维护
presentingView
的父视图,第三正值的动画片控制器必须为这个改造。显然,这样的代价是无能为力承受的。

小结:经过地方的尝试,建议是,不要干涉官方对 Modal
转场的拍卖,我们错过适应它。在 Custom 模式下,由于 presentingView 不给
containerView 管理,在 dismissal 转场中并非像任何的转场那样将
toView(presentingView) 加入 containerView,否则 presentingView
将熄灭不见,而以则也杀可能假死;而当 presentation
转场中,切记不要手动将 fromView(presentingView) 移出其父视图。

iOS 8
<UIViewControllerContextTransitioning>共谋上加了viewForKey:主意为有益得
fromView 和 toView,但是在 Modal 转场里要是小心,从地方可以理解,Custom
模式下,presentingView 并无让 containerView
管理,这时通过viewForKey:计来博 presentingView 得到的凡
nil,必须通过viewControllerForKey:获得 presentingVC 后来获得。因此当
Modal 转场中,较稳当的点子是打 fromVC 和 toVC 中得到 fromView 和
toView。

顺带一提,前面提到的UIView的切近措施transitionFromView:toView:duration:options:completion:会以
Custom 模式下工作,却和 FullScreen 模式有点不匹配。

裁撤转场

互动控制动画时发或吃撤,这频繁带动两单问题:恢复数据与恶化动画。

此地用恢复的数目是selectedIndex,我们以彼此转场开始前备份当前之selectedIndex,如果转场取消了就算用此备份数据恢复。逆转动画反而看起比较难以解决。

在地方的 pan
手势处理办法吃,我们怎么样逆转动画的运作为?既然speed也0时卡通静止不动,调整呢负数是否足以兑现逆播放呢?不可知,效果是视图消失不见。不过我们尚得调动timeOffset性能,从当前价一直恢复到0。问题是哪发生动画的功用?动画的本色是看看图属性在某段时间内的连年变,当然者连续变并无是绝对的接连,只要时间距离够不够,变化之功效就算会流畅得看上去是连连变,在此间为这转变频率与屏幕的基础代谢同步即可,CADisplayLink得助我们贯彻即点,它可以以屏幕刷新时之各一样轴执行绑定的方式:

//在上面的/*逆转动画*/处添加以下两行代码:
let displayLink = CADisplayLink(target: self, selector: "reverseAnimation:")
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)

func reverseAnimation(displayLink: CADisplayLink){
    //displayLink.duration表示每一帧的持续时间,屏幕的刷新频率为60,duration = 1/60。
    //这行代码计算的是,屏幕刷新一帧后,timeOffset 应该回退一帧的时间。
    let timeOffset = view.layer.timeOffset - displayLink.duration
    if timeOffset > 0{
        containerView.layer.timeOffset = timeOffset
    }else{
        //让 displayLink 失效,停止对当前方法的调用。
        displayLink.invalidate()
        //回到最初的状态。
        containerView.layer.timeOffset = 0
        //speed 恢复为1后,视图立刻跳转到动画的最终状态。
        containerView.layer.speed = 1
    }
}

末了一词代码会叫人纳闷,为何让视图恢复也末段状态,与我们的初衷南辕北辙。speed非得恢复为1,不然后续发起的转场动画无法顺利推行,视图也无能为力响应触摸事件,直接原因未知。但speed卷土重来为1继会产出一个题材:由于在原的动画里
fromView 最终会受转移出屏幕,尽管 Slide 动画控制器 UIView 动画里的
completion handle 里会还原 fromView 和 toView
的状态,这种状态的急转直下会招闪屏现象。怎么化解?添加一个借的 fromView 到
containerView替代已给移出屏幕外之真的
fromView,然后在异常缺乏的时光间隔后将之变除,因为这时 fromView
已经归位。在还原speed继加加以下代码:

let fakeFromView = privateFromViewController.view.snapshotViewAfterScreenUpdates(false)
containerView.addSubview(fakeFromView)
performSelector("removeFakeFromView:", withObject: fakeFromView, afterDelay: 1/60)
//在 Swift 中动态调用私有方法会出现无法识别的选择器错误,解决办法是将私有方法设置为与 objc 兼容,需要添加 @objc 修饰符。
@objc private func removeFakeFromView(fakeView: UIView){
    fakeView.removeFromSuperview()
}

透过试验,上面用来控制及收回 UIView 动画的方法吗适用于用 Core Animation
实现的动画片,毕竟 UIView 动画是因此 Core Animation
实现之。不过,我们于前面提到过,官方对 Core Animation
实现之互转场动画的支撑有欠缺,估计官方鼓励采用更尖端的接口吧,因为转场动画结束后要调用transitionContext.completeTransition(!isCancelled),而采用
Core Animation
完成这等同步要开展适度的配备,实现之门道发点儿种植都实现并无略,相比之下
UIView 动画使用 completion block
对斯展开了打包,使用十分有利。转场协议的构造就比较复杂了,选择 UIView
动画能够肯定降低实现资本。

方的兑现忽略了一个细节:时间曲线。逆转动画时每一样幅都回退相同之年月,也就是说,逆转动画的岁月曲线是线性的。交互控制器的磋商<UIViewControllerInteractiveTransitioning>还有少独可摘方式:

optional func completionCurve() -> UIViewAnimationCurve
optional func completionSpeed() -> CGFloat

及时有限个措施记录了动画片采用的动画片曲线和速,在恶化动画时如能冲这二者计算出当前帧应该回退的时光,那么就是可知促成宏观的恶化,显然这是一个数学问题。恩,我们越了之细节吧,因为我数学不好,讨论这题目大吃力。推荐阅读
Objc.io 的交互式动画一和,该文探讨了安打造自然真实的交互式动画。

说道上了

模仿 UITabBarControllerDelegate 协议的 ContainerViewControllerDelegate
协议:

//在 Swift 协议中声明可选方法必须在协议声明前添加 @objc 修饰符。
@objc protocol ContainerViewControllerDelegate{
    func containerController(containerController: SDEContainerViewController, animationControllerForTransitionFromViewController 
                                          fromVC: UIViewController, 
                           toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
    optional func containerController(containerController: SDEContainerViewController, interactionControllerForAnimation 
                                      animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
}

每当容器控制器SDEContainerViewController接近吃,添加转场代理属性:

weak var containerTransitionDelegate: ContainerViewControllerDelegate?

代办的一定就是是供动画控制器和相控制器,系统打包的UIPercentDrivenInteractiveTransition恍如才是调用了转场环境目标的呼应措施而已,执行navigationController.pushViewController(toVC, animated: true)立好像语句子触发转场后
UIKit
就接管了剩余的事务,再汇总文档的描述,可知道转场环境就是贯彻即时周的核心。

每当文章前面的部分里转场环境目标的意只是提供关乎转场过程的音与状态,现在要我们贯彻该协议,并且实现藏的那片任务。
<UIViewControllerContextTransitioning>商量里之大举方式还是必实现的,不过本咱们先行实现非交互转场的组成部分,实现这是不行粗略的,主要是调用动画控制器执行转场动画。在「实现分析」一节省里我们视实现转场的代码只发生十几实行要曾,动画控制器需要开的只是处理视图和动画片的片段,转场环境目标则要负责管理子
VC,通过SDEContainerViewController供 containerView 以及 fromVC 和
toVC,实现并无是难题。显然由咱们兑现之自定义容器 VC
来提供转场环境目标是无限适度的,并且转场环境目标应当是私房的,其初始化方法极其启动转场的措施如下:

class ContainerTransitionContext: NSObject, UIViewControllerContextTransitioning{
    init(containerViewController: SDEContainerViewController, 
                   containerView: UIView, 
       fromViewController fromVC: UIViewController, 
           toViewController toVC: UIViewController){...}

    //非协议方法,是启动非交互式转场的便捷方法。
    func startNonInteractiveTransitionWith(delegate: ContainerViewControllerDelegate){
        //转场开始前添加 toVC,转场动画结束后会调用 completeTransition: 方法,在该方法里完成后续的操作。
        self.privateContainerViewController.addChildViewController(privateToViewController)
        //通过 ContainerViewControllerDelegate 协议定义的方法生成动画控制器,方法名太长了略去。
        self.privateAnimationController = delegate.XXXmethod
        //启动转场并执行动画。
        self.privateAnimationController.animateTransition(self)
    }
    //协议方法,动画控制器在动画结束后调用该方法,完成管理子 VC 的后续操作,并且考虑交互式转场可能取消的情况撤销添加的子 VC。
    func completeTransition(didComplete: Bool) {
        if didComplete{
            //转场完成,完成添加 toVC 的工作,并且移除 fromVC 和 fromView。
            self.privateToViewController.didMoveToParentViewController(privateContainerViewController)
            self.privateFromViewController.willMoveToParentViewController(nil)
            self.privateFromViewController.view.removeFromSuperview()
            self.privateFromViewController.removeFromParentViewController()
        }else{
            //转场取消,移除 toVC 和 toView。
            self.privateToViewController.didMoveToParentViewController(privateContainerViewController)
            self.privateToViewController.willMoveToParentViewController(nil)
            self.privateToViewController.view.removeFromSuperview()
            self.privateToViewController.removeFromParentViewController()
        }
        //非协议方法,处理收尾工作:如果动画控制器实现了 animationEnded: 方法则执行;如果转场取消了则恢复数据。
        self.transitionEnd()
    }
}

SDEContainerViewController接近吃,添加转场环境性:

private var containerTransitionContext: ContainerTransitionContext?

并修改transitionViewControllerFromIndex:toIndex道实现由定义容器 VC
转场动画:

private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
    if self.containerTransitionDelegate != nil{
        let fromVC = viewControllers![fromIndex]
        let toVC = viewControllers![toIndex]
        self.containerTransitionContext = ...//利用 fromVC 和 toVC 初始化。
        self.containerTransitionContext?.startNonInteractiveTransitionWith(containerTransitionDelegate!)
    }else{/*没有提供转场代理的话,则使用最初没有动画的转场代码,或者提供默认的转场动画*/}
}

如此这般我们就是下协议落实了于定义容器控制器的转场动画,可以使用第三正在的卡通控制器来促成不同之作用。

不过假如留心及时几单对象中错综复杂的援关系避免引用循环,关系图如下:

365体育网址 2

iOS 视图控制器转场详解,ios视图转场详解

   

iOS 8的改进:UIPresentationController

iOS 8 针对分辨率日益分裂的 iOS
设备带来了新的适应性布局方案,以往略专为在 iPad 上规划的控制器也能于
iPhone
上应用了,一个不行转变是当视图控制器的(模态)显示过程,包括转场过程,引入了UIPresentationController类似,该类接管了
UIViewController 的显示过程,为其提供转场和视图管理支持。当
UIViewController
modalPresentationStyle属性为.Custom时(不支持.FullScreen),我们发空子通过控制器的转场代理提供UIPresentationController的子类对
Modal
转场进行更的定制。官方对此类参与转场的流程与采用办法来特别详细的验证:Creating
Custom
Presentations。

UIPresentationController恍如重要被 Modal 转场带来了以下几点变化:

  1. 定制 presentedView 的外观:设定 presentedView 的尺码和当
    containerView 中上加于定义视图并为这些视图添加动画;
  2. 得选择是否移除 presentingView;
  3. 好当匪待动画控制器的情状下独自工作;
  4. iOS 8 中之适应性布局。

以上变化着第1接触 iOS 7 中呢能得,3及4是 iOS 8
带来的新特性,只有第2沾才真的化解了 iOS 7 中的痛点。在 iOS 7
中定制外观时,动画控制器需要负责管理额外添加的底视图,UIPresentationController看似将该意义剥离了出去独立承担,其提供了之类的道与转场,对转场过程实现了越发缜密的决定,从命名就足以望与动画片控制器里之animateTransition:的关系:

func presentationTransitionWillBegin()
func presentationTransitionDidEnd(_ completed: Bool)
func dismissalTransitionWillBegin()
func dismissalTransitionDidEnd(_ completed: Bool)

除了
presentingView,UIPresentationController恍如具有转场过程遭到剩下的角色:

//指定初始化方法。
init(presentedViewController presentedViewController: UIViewController, presentingViewController presentingViewController: UIViewController)
var presentingViewController: UIViewController { get }
var presentedViewController: UIViewController { get }
var containerView: UIView? { get }
//提供给动画控制器使用的视图,默认返回 presentedVC.view,通过重写该方法返回其他视图,但一定要是 presentedVC.view 的上层视图。
func presentedView() -> UIView?     

没有 presentingView 是因为 Custom 模式下 presentingView 不受
containerView 管理,UIPresentationController接近并没有变动就或多或少。iOS 8
扩充了转场环境协议,可以透过viewForKey:便宜得转场的视图,而该方法在
Modal 转场中获之是presentedView()返回的视图。因此我们好当子类中将
presentedView 包装在旁视图后再行写该方法返回包装后的视图当做
presentedView 在动画控制器中以。

接下来,我用UIPresentationController子类实现达标一样省「Modal
转场实践」里的效能,presentingView 和 presentedView
的卡通由动画控制器负责,剩下的事务可以交给我们落实之子类来好。

与角色还备好了,但产生个问题,无法直接访问动画控制器,不亮转场的持续时间,怎么和转场过程同步?这时候前面提到的用途大少之转场协调器(Transition
Coordinator)将在这里派上用场。该对象只是经 UIViewController
transitionCoordinator()方式赢得,这是 iOS 7 为从定义转场新增的
API,该法只有于控制器处于转场过程被才回到一个以及时转场有关的有效性对象,其他时候回来
nil。

转场协调器遵守<UIViewControllerTransitionCoordinator>合计,它富含以下几独办法:

//与动画控制器中的转场动画同步,执行其他动画
animateAlongsideTransition:completion:
//与动画控制器中的转场动画同步,在指定的视图内执行动画
animateAlongsideTransitionInView:animation:completion:

出于转场协调器的这种特征,动画的同台问题解决了。

class OverlayPresentationController: UIPresentationController {
    let dimmingView = UIView()

    //Presentation 转场开始前该方法被调用。
    override func presentationTransitionWillBegin() {
        self.containerView?.addSubview(dimmingView)

        let initialWidth = containerView!.frame.width*2/3, initialHeight = containerView!.frame.height*2/3
        self.dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        self.dimmingView.center = containerView!.center
        self.dimmingView.bounds = CGRect(x: 0, y: 0, width: initialWidth , height: initialHeight)
        //使用 transitionCoordinator 与转场动画并行执行 dimmingView 的动画。
        presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in
            self.dimmingView.bounds = self.containerView!.bounds
        }, completion: nil)
    }
    //Dismissal 转场开始前该方法被调用。添加了 dimmingView 消失的动画,在上一节中并没有添加这个动画,
    //实际上由于 presentedView 的形变动画,这个动画根本不会被注意到,此处只为示范。
    override func dismissalTransitionWillBegin() {
        presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in
            self.dimmingView.alpha = 0.0
            }, completion: nil)
    }    
}

OverlayPresentationController恍如接手了 dimmingView
的行事后,需要回到上亦然省OverlayAnimationController里将干 dimmingView
的有的去,然后以 presentedVC
的转场代理属性transitioningDelegate受提供此类实例就足以实现与直达一致节省一样的职能。

func presentationControllerForPresentedViewController(_ presented: UIViewController, 
                              presentingViewController presenting: UIViewController, 
                                      sourceViewController source: UIViewController) -> UIPresentationController?{
    return OverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}

在 iOS 7 中,Custom 模式的 Modal 转场里,presentingView
不会见叫移除,如果我们要移除它并妥善恢复会毁动画控制器的独立性使得第三着动画控制器无法直接下;在
iOS 8
中,UIPresentationController缓解了当下点,给予了我们选取的权位,通过再写下面的法子来决定
presentingView 是否当 presentation 转场结束晚受移除:

func shouldRemovePresentersView() -> Bool

归来 true 时,presentation 结束晚 presentingView 被移除,在 dimissal
结束后 UIKit 会自动将 presentingView
恢复至原来的视图结构中。通过UIPresentationController的与,Custom
模式完全实现了 FullScreen 模式下之满贯特点。

乃或会见纳闷,除了解决了 iOS 7中无法干预 presentingView
这个痛点外,还有呀说辞值得咱们采取UIPresentationController恍如?除了能够和动画片控制器配合,UIPresentationController类似为能够脱动画控制器独立工作,在转场代理里我们唯有提供后者为会针对
presentedView 的外观进行定制,缺点是无法控制 presentedView
的转场动画,因为就是动画片控制器的职责,这种情形下,presentedView
的转场动画采用的凡默认的卡通片效果,转场协调器实现之动画片则是行使默认的卡通时间。

iOS 8
带来了适应性布局,<UIContentContainer>共谋用于应视图尺寸变化及屏幕旋转事件,之前用于拍卖屏幕旋转的主意还让抛弃了。UIViewController
和 UIPresentationController 类都守该谋,在 Modal
转场中如果提供了后者,则是因为后者负责前者的尺寸变和屏幕旋转,最终的布局时吗于后者里。在OverlayPresentationController面临再写以下方式来调动视图布局和应对屏幕旋转:

override func containerViewWillLayoutSubviews() {
    self.dimmingView.center = self.containerView!.center
    self.dimmingView.bounds = self.containerView!.bounds

    let width = self.containerView!.frame.width * 2 / 3, height = self.containerView!.frame.height * 2 / 3
    self.presentedView()?.center = self.containerView!.center
    self.presentedView()?.bounds = CGRect(x: 0, y: 0, width: width, height: height)
}

UINavigationControllerDelegate

定制 UINavigationController
这种容器控制器的转场时,很合乎实现一个子类,自身集转场代理,动画控制器于寥寥,也方便使用,不过这样做有时候又限制了她的运范围,别人为落实了好的子类时就不能够方便使用你的功能,这里用的凡以转场代理封装成一个近乎。

class SDENavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
    //在<UINavigationControllerDelegate>对象里,实现该方法提供动画控制器,返回 nil 则使用系统默认的效果。
    func navigationController(navigationController: UINavigationController, 
         animationControllerForOperation operation: UINavigationControllerOperation, 
                         fromViewController fromVC: UIViewController, 
                             toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        //使用上一节实现的 Slide 动画控制器,需要提供操作类型信息。
        let transitionType = SDETransitionType.NavigationTransition(operation)
        return SlideAnimationController(type: transitionType)
    }
}

一旦你当代码里呢您的控制器里这么设置代理:

//错误的做法,delegate 是弱引用,在离开这行代码所处的方法范围后,delegate 将重新变为 nil,然后什么都不会发生。
self.navigationController?.delegate = SDENavigationControllerDelegate()

可以下高引用的变量来引用新实例,且未可知采用当地变量,在控制器中新增一个变量来维持新实例就可了。

self.navigationController?.delegate = strongReferenceDelegate

缓解了身故引用的题目,这行代码应该置身哪里执行呢?很多总人口好以viewDidLoad()做片布局工作,但于此地装无法担保是可行之,因为这时候控制器可能没有进入
NavigationController 的控制器栈,self.navigationController归来的或者是
nil;如果是经过代码 push 其他控制器,在 push
前安设即可;prepareForSegue:sender:措施是转场前改动设置的末尾一浅机遇,可以于此装;保险点,使用UINavigationController子类,自己作为代理,省去到处设置的麻烦。

不过,通过代码设置终究显得非常烦且未安全,在 storyboard
里装同样劳永逸:在控件库里拖拽一个 NSObject 对象及有关的
UINavigationControler
上,在控制面板里拿其项目设置也SDENavigationControllerDelegate,然后拖拽鼠标将那个设置为代理。

终极一步,像往常平触发转场:

self.navigationController?.pushViewController(toVC, animated: true)//or
self.navigationController?.popViewControllerAnimated(true)

在 storyboard 中经安装 segue 时开始启动画吗用张同样的 Slide 动画。Demo
地址:NavigationControllerTransition。

Modal 转场实践

UIKit 已经为 Modal 转场实现了多作用,当 UIViewController
modalPresentationStyle属性为.Custom.FullScreen时,我们便发出机遇定制转场效果,此时modalTransitionStyle点名的转场动画将见面被忽略。

Modal 转场开放由定义功能后最为令人谢兴趣的凡定制 presentedView
的尺寸,下面来咱们来促成一个拉动暗色调背景的稍窗口功能。Demo
地址:CustomModalTransition。

365体育网址 3

鉴于需要保持 presentingView 可见,这里的 Modal 转场应该利用
UIModalPresentationCustom 模式,此时 presentedVC
modalPresentationStyle属于性值应安装也.Custom。而且同容器 VC
的转场的代理由容器 VC 自身的代办提供不同,Modal 转场的代理由 presentedVC
提供。动画控制器的骨干代码:

class OverlayAnimationController: NSobject, UIViewControllerAnimatedTransitioning{
    ... 
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {            
        ...
        //不像容器 VC 转场里需要额外的变量来标记操作类型,UIViewController 自身就有方法跟踪 Modal 状态。
        //处理 Presentation 转场:
        if toVC.isBeingPresented(){
            //1
            containerView.addSubview(toView)
            //在 presentedView 后面添加暗背景视图 dimmingView,注意两者在 containerView 中的位置。
            let dimmingView = UIView()
            containerView.insertSubview(dimmingView, belowSubview: toView)

            //设置 presentedView 和 暗背景视图 dimmingView 的初始位置和尺寸。
            let toViewWidth = containerView.frame.width * 2 / 3
            let toViewHeight = containerView.frame.height * 2 / 3
            toView.center = containerView.center
            toView.bounds = CGRect(x: 0, y: 0, width: 1, height: toViewHeight)

            dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
            dimmingView.center = containerView.center
            dimmingView.bounds = CGRect(x: 0, y: 0, width: toViewWidth, height: toViewHeight)

            //实现出现时的尺寸变化的动画:
            UIView.animateWithDuration(duration, delay: 0, options: .CurveEaseInOut, animations: {
                toView.bounds = CGRect(x: 0, y: 0, width: toViewWidth, height: toViewHeight)
                dimmingView.bounds = containerView.bounds
                }, completion: {_ in
                    //2
                    let isCancelled = transitionContext.transitionWasCancelled()
                    transitionContext.completeTransition(!isCancelled)
            })
        }
        //处理 Dismissal 转场,按照上一小节的结论,.Custom 模式下不要将 toView 添加到 containerView,省去了上面标记1处的操作。
        if fromVC.isBeingDismissed(){
            let fromViewHeight = fromView.frame.height
            UIView.animateWithDuration(duration, animations: {
                fromView.bounds = CGRect(x: 0, y: 0, width: 1, height: fromViewHeight)
                }, completion: { _ in
                    //2
                    let isCancelled = transitionContext.transitionWasCancelled()
                    transitionContext.completeTransition(!isCancelled)
            })
        }
    }
}
动画片控制与 CAMediaTiming 协议

本条协议定义了相同效时间体系,是控制动画进度的要。UIView Animation
是采取 Core Animation 框架实现的,也即是使用 UIView 的 CALayer
对象实现的动画片,而 CALayer 对象遵守该谋。

于交互控制器的小节里自己打了一个比方,交互控制器就如一个视频播放器一样控制着转场动画这个视频的速度。依靠
CAMediaTiming 这套商,我们得以于 CALayer
对象上针对长的卡通片实现控制。官方的落实充分有或也是使用了一致的招。CAMediaTiming
协议被发生以下几独属性:

//speed 作用类似于播放器上控制加速/减速播放,默认为1,以正常速度播放动画,为0时,动画将暂停。
var speed: Float 
//修改 timeOffset 类似于拖动进度条,对一个2秒的动画,该属性为1的话,动画将跳到中间的部分。
//但当动画从中间播放到预定的末尾时,会续上0秒到1秒的动画部分。
var timeOffset: CFTimeInterval
//动画相对于父 layer 延迟开始的时间,这是一个实际作用比字面意义复杂的属性。 
var beginTime: CFTimeInterval  

Core Animation 的文档中提供了怎么暂停与死灰复燃动画的示范:How to pause the
animation of a layer
tree。我们以的运实现对快的控制,这种方式对中的子视图上添加的卡通片也克落实控制,这多亏我们需要的。假设以
containerView 中之 toView 上推行一个概括的沿 X 轴方向移动 100
单位之各项移动画,由executeAnimation()措施执行。下面是运用手势控制该动画进度的着力代码:

func handlePan(gesture: UIPanGestureRecognizer){
    switch gesture.state{
    case .Began:
        //开始动画前将 speed 设为0,然后执行动画,动画将停留在开始的时候。
        containerView.layer.speed = 0
        //在transitionContext里,这里替换为 animator.animateTransition(transitionContext)。
        executeAnimation() 
    case .Changed:
        let percent = ...
        //此时 speed 依然为0,调整 timeOffset 可以直接调整动画的整体进度,这里的进度控制以时间计算,而不是比例。
        containerView.layer.timeOffset = percent * duration
    case .Ended, .Cancelled:
        if progress > 0.5{
            //恢复动画的运行不能简单地仅仅将 speed 恢复为1,这是一套比较复杂的机制。
            let pausedTime = view.layer.timeOffset
            containerView.layer.speed = 1.0 
            containerView.layer.timeOffset = 0.0
            containerView.layer.beginTime = 0.0
            let timeSincePause = view.layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
            containerView.layer.beginTime = timeSincePause
        }else{/*逆转动画*/}
        default:break
    }
}

相互之间转场的限量

倘希望转场中之卡通能到地叫交互控制,必须满足2独隐性条件:

  1. 使 UIView 动画的 API。你本来为堪下 Core Animation
    来促成动画,甚至,这种动画可以给交互控制,但是当彼此中止时,会并发有竟状况:如果您是地用
    Core Animation 的法复现了 UIView
    动画的功能(不仅仅是卡通片,还包动画结束后的拍卖),那么手势结束晚,动画将直接跨越反到最终状态;而更多之等同种情景是,你连从未对地复现
    UIView
    动画的力量,手势结束后动画会停留在手势中止时之状态,界面失去响应。所以,如果你得全面的竞相转场动画,必须采取
    UIView 动画。
  2. 在动画控制器的animateTransition:备受交动画。问题及第1点类似,在viewWillDisappear:然的道吃交的动画也克于交互控制,但相停止时,立即跳反到最后状态。

假如您盼做多流动画,在某某动画结束晚还实践另外一段子动画,可以由此
UIView Block Animation 的 completion
闭包来实现动画链,或者是通过设定动画执行的延迟时间使得不同动画错分开来,但是彼此转场不支持即时简单栽样式。UIView
的 keyFrame Animation API
可以助你,通过在动画过程的例外时节点上加要帧动画就足以兑现多流动画。我实现了一个这么的大半路转场动画,Demo
在是:CollectionViewAlbumTransition。

UITabBarControllerDelegate

一致当容器控制器,UITabBarController 的转场代理和
UINavigationController
类似,通过类似之办法供动画控制器,不过<UINavigationControllerDelegate>的代办方里供了操作类型,但<UITabBarControllerDelegate>的代理方无供滑动的来头信息,需要我们来获得滑动的样子。

class SDETabBarControllerDelegate: NSObject, UITabBarControllerDelegate {
    //在<UITabBarControllerDelegate>对象里,实现该方法提供动画控制器,返回 nil 则没有动画效果。
    func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController 
                                    fromVC: UIViewController, 
                     toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
        let fromIndex = tabBarController.viewControllers!.indexOf(fromVC)!
        let toIndex = tabBarController.viewControllers!.indexOf(toVC)!

        let tabChangeDirection: TabOperationDirection = toIndex < fromIndex ? .Left : .Right
        let transitionType = SDETransitionType.TabTransition(tabChangeDirection)
        let slideAnimationController = SlideAnimationController(type: transitionType)
        return slideAnimationController
    }
}

也 UITabBarController 设置代理的道与陷阱和方的
UINavigationController 类似,注意delegate性之辞世引用问题。点击 TabBar
的邻座页面进行切换时,将会见见到 Slide
动画;通过以下代码触发转场时也将看到同一的职能:

tabBarVC.selectedIndex = ...//or
tabBarVC.selectedViewController = ...

Demo 地址:ScrollTabBarController。

前言

屏幕左边缘右滑返回,TabBar
滑动切换,你是不是喜欢并充分负就有限只操作,甚至看 App
不支持即好像操作的说话简直反人类?这半单操作以大屏时代大提升了操作效率,其背后的技巧就是是今日的主题:视图控制器转换(View
Controller Transition)。

探望图控制器中之视图显示在屏幕上闹点儿种植方式:最要紧的方是内嵌在容器控制器中,比如
UINavigationController,UITabBarController,
UISplitController;由另外一个视图控制器显示它,这种方式一般为称为模态(Modal)显示。View
Controller Transition 是呀?在 NavigationController 里 push 或 pop
一个 View Controller,在 TabBarController 中切换至另外 View
Controller,以 Modal 方式展示另外一个 View Controller,这些还是 View
Controller Transition。在 storyboard 里,每个 View Controller 是一个
Scene,View Controller Transition 便是从一个 Scene 转换到另外一个
Scene;为便宜,以下对 View Controller Transition 的国语称呼采用
Objccn.io 中之翻「转场」。

当 iOS 7
之前,我们只能使用系统提供的转场效果,大部分时够用,但一味是十足而已,总归会出各种非令人满意的小地方,但我们倒无力改变;iOS
7 开放了有关 API
允许我们本着转场效果进行宏观定制,这顶强了,自定义转场动画及对相互手段的支持带动了极其可能。

正文并非豪华的转场动画教程,相反,文中的转场动画效果都格外简单易行,但本文的内容连无略,我以带动您追转场背后的建制,缺陷和贯彻过程遭到的技巧和陷阱。阅读本文需要读者至少要针对性
ViewController 和 View
的结构与协和来基本的询问,最好自己亲手促成了一两种转场动画。如果您对斯感到没有信心,推荐观看官方文档:View
Controller Programming Guide for
iOS,学习之文档将会给您再度便于掌握本文的内容。对你想上的小节,我盼望而自己亲手写下这些代码,一步步地扣押在力量是如何落实之,至少对本人而言,看各种有关材料时才发生字面意思上的掌握,正是一步步之考才会于我懂每一个步骤。本文涉及的内容比较多,为了避免篇幅过长,我独自让起第一代码而非是由新建工程上马让你各个一个步骤。本文基于
Xcode 7 以及 Swift 2,Demo
合集地址:iOS-ViewController-Transition-Demo。

特殊的 Modal 转场

动画片控制器协议

卡通控制器负责上加视图以及实践动画,遵守UIViewControllerAnimatedTransitioning协议,该谋要求兑现以下办法:

//执行动画的地方,最核心的方法。
(Required)func animateTransition(_ transitionContext: UIViewControllerContextTransitioning)
//返回动画时间,"return 0.5" 已足够,非常简单,出于篇幅考虑不贴出这个方法的代码实现。
(Required)func transitionDuration(_ transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
//如果实现了,会在转场动画结束后调用,可以执行一些收尾工作。
(Optional)func animationEnded(_ transitionCompleted: Bool)

最为关键之是第一个方式,该方法接受一个死守<UIViewControllerContextTransitioning>谋的转场环境目标,上亦然省之
API
解释里关系这协议,它提供了转移场所得之重中之重数据:参与转场的视图控制器和转场过程的状态信息。

UIKit
以转场开始前别遵守转场环境协议<UIViewControllerContextTransitioning>的目标
transitionContext,它发生以下几个法子来供动画控制器需要的音信:

//返回容器视图,转场动画发生的地方。
func containerView() -> UIView?
//获取参与转场的视图控制器,有 UITransitionContextFromViewControllerKey 和 UITransitionContextToViewControllerKey 两个 Key。 
func viewControllerForKey(_ key: String) -> UIViewController?
//iOS 8新增 API 用于方便获取参与参与转场的视图,有 UITransitionContextFromViewKey 和 UITransitionContextToViewKey 两个 Key。
func viewForKey(_ key: String) -> UIView? AVAILABLE_IOS(8_0)

通过viewForKey:取得之视图是viewControllerForKey:回去的控制器的根视图,或者
nil。viewForKey:措施返回 nil 只来雷同栽状况: UIModalPresentationCustom
模式下的 Modal 转场 ,通过这个方法赢得 presentingView 时得到的将凡
nil,在后边的 Modal 转场里会详细解释。

前提到转场的面目是产一个观的视图替换当前光景的视图,从即场景过渡下一个现象。下面称将消失的气象的视图为
fromView,对应之视图控制器为 fromVC,即将出现的视图为
toView,对应之视图控制器称之为
toVC。几种植转场方式的转场操作都是可逆的,一种操作里的 fromView 和 toView
在逆向操作里之角色互换成对方,fromVC 和 toVC
也是这样。于动画控制器里,参与转场的视图只有 fromView 和 toView
之分,与转场方式无关。转场动画的末尾效果就限制为你的想象力。
立即也是卡通片控制器在卷入后可为第三正在使用的首要原由。

于 iOS 8 中但是经以下方法来获得与转场的老三只重大视图,在 iOS 7
中则要通过相应之视图控制器来博取,为免 API
差异导致代码过长,示例代码中一直用下的视图变量:

let containerView = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)

落实交互化

每当非交互转场的基础及以的至互化需要少只极:

满足以上两独规范十分简单,但是很轻犯错误。

不错地提供相互控制器

倘当转场代理中提供了彼此控制器,而转场发生常连无办法来叫转场进程(比如手势),转场过程将一直处于开始流无法收场,应用界面也会错过响应:在
NavigationController 中点击 NavigationBar 也能够实现 pop
返回操作,但这从不了互手段的支持,转场过程卡壳;在 TabBarController
的代理里供相互控制器是同样的问题,点击 TabBar
切换页面时也从不实现相互之间控制。因此才于委处于交互状态时才提供相互控制器,可以使用一个变量来号交互状态,该变量由交互手势来更新状态。

觉得 NavigationController 提供相互控制器为例:

class SDENavigationDelegate: NSObject, UINavigationControllerDelegate {
    var interactive = false
    let interactionController = UIPercentDrivenInteractiveTransition()
    ...

    func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController 
                               animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactive ? self.interactionController : nil
    }
}

TabBarController 的兑现类似,Modal 转场代理分别吗 presentation 和
dismissal 提供了各自的互控制器,也用专注点的问题。

题材之根源是互相控制的劳作体制导致的,交互过程实际上是出于转场环境目标<UIViewControllerContextTransitioning>来管理之,它提供了如下几独艺术来支配转场的进度:

func updateInteractiveTransition(_ percentComplete: CGFloat)//更新转场进度,进度数值范围为0.0~1.0。
func cancelInteractiveTransition()//取消转场,转场动画从当前状态返回至转场发生前的状态。
func finishInteractiveTransition()//完成转场,转场动画从当前状态继续直至结束。

相互之间控制协议<UIViewControllerInteractiveTransitioning>独自发生一个要贯彻之措施:

func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning)

当转场代理里供了交互控制器后,转场开始经常,该方式自动为 UIKit
调用对转场环境进行配置。

系由包好的UIPercentDrivenInteractiveTransition受到之操纵转场进度的主意及转场环境目标提供的老三独法子同名,实际上不过是前者调用了后者的方而已。系统以平等栽解耦的法使动画控制器,交互控制器,转场环境目标互相合作,我们只有待采取UIPercentDrivenInteractiveTransition的老三只同名方法来决定速度就够用了。如果您如促成自己之彼此控制器,而不是UIPercentDrivenInteractiveTransition的子类,就用调用转场环境的老三个点子来支配速度,压轴环节我们以示范怎么做。

交互控制器控制转场的经过就如以动画片控制器实现的动画片制作成一部视频,我们以手势或是其他艺术来决定转场动画的播报,可以发展,后退,继续要停止。finishInteractiveTransition()法吃调用后,转场动画从即底状态将继续拓展直到动画结束,转场完成;cancelInteractiveTransition()吃调用后,转场动画从眼前底状态回拨到起状态,转场取消。

当 NavigationController 中点击 NavigationBar 的 backBarButtomItem 执行
pop 操作时,由于我们无法介入 backBarButtomItem
的中间流程,就失去控制速度的招数,于是转场过程就生一个方始,永远不会见结。其实我们特需要出能实践上述几只方式的伎俩就是可以针对转场动画进行支配,用户与屏幕的相互手段里,手势是贯彻此决定过程的天赋手段,我猜测这是该给称交互控制器的原委。

相互之间手段之相当

下采用演示如何下屏幕边缘滑动手势UIScreenEdgePanGestureRecognizer
NavigationController 中控制 Slide
动画控制器提供的动画片来实现右滑返回的机能,该手势绑定的动作方法如下:

func handleEdgePanGesture(gesture: UIScreenEdgePanGestureRecognizer){
    //根据移动距离计算交互过程的进度。
    let percent = ...
    switch gesture.state{
    case .Began:
        //转场开始前获取代理,一旦转场开始,VC 将脱离控制器栈,此后 self.navigationController 返回的是 nil。
        self.navigationDelegate = self.navigationController?.delegate as? SDENavigationDelegate
        //更新交互状态
        self.navigationDelegate?.interactive = true
        //1.交互控制器没有 start 之类的方法,当下面这行代码执行后,转场开始;
        //如果转场代理提供了交互控制器,它将从这时候开始接管转场过程。
        self.navigationController?.popViewControllerAnimated(true)
    case .Changed:
        //2.更新进度:
        self.navigationDelegate?.interactionController.updateInteractiveTransition(percent)
    case .Cancelled, .Ended:
        //3.结束转场:
        if percent > 0.5{
            //完成转场。
            self.navigationDelegate?.interactionController.finishInteractiveTransition()
        }else{
            //或者,取消转场。
            self.navigationDelegate?.interactionController.cancelInteractiveTransition()
        }
        //无论转场的结果如何,恢复为非交互状态。
        self.navigationDelegate?.interactive = false
    default: self.navigationDelegate?.interactive = false
    }
}

相互之间转场的流水线虽是三处数字符号的代码。不管是啊交互方式,使用什么转场方式,都是以行使这三单艺术控制转场的快慢。对交互式转场,交互手段只是表现形式,本质是让转场进程。万分希望能看出更新颖的竞相手段,比如通过点击页面不同区域来支配一样套复杂的流水线动画。TabBarController
的 Demo 中为落实了滑动切换 Tab 页面,代码是近乎的,就不占篇幅了;示范的
Modal
转场我并未啊底实现互动控制,原因为波及了了,没有比相符操作直觉的竞相手段,不过真要为那个长交互控制,代码和上面是相近的。

转场交互化后结果发生半点种植:完成和收回。取消后动画将会原本行程回到开状态,但就变化了的数额怎么回复?

一致栽情景是,控制器的网性能,比如,在 TabBarController
里使用方面的法子实现滑动切换 Tab
页面,中途取消的话,已经变化的selectedIndex性该怎么过来为原值;上面的代码里,取消转场的代码执行后,self.navigationController返的照样要是
nil,怎么给控制器回到 NavigationController
的控制器栈顶。对于这种情形,UIKit
自动为我们回复了,不欲我们担心(可能你都没有察觉及当时回事);

此外一种植就是,转场发生的进程遭到,你或想实现某些意义,一般是于下面的波中推行,转场中途取消的说话或得取消这些职能。

func viewWillAppear(_ animated: Bool)
func viewDidAppear(_ animated: Bool)
func viewWillDisappear(_ animated: Bool)
func viewDidDisappear(_ animated: Bool)

彼此转场介入后,视图在这些状态之中的变变得复杂,WWDC
上苹果之工程师还代表转场过程中 view
Will有关道与Did连锁道的执行各个并无可知收获保险,虽然几率领非常有点,但一旦你依靠让这些方法执行之逐条的语就是可能需要注意就点。而且,Did有关道调用时连无意味转场过程着实收了。另外,fromView
和 toView
之间的当下几乎种植艺术的对立顺序更加混乱,具体的案例可以参考这里:The
Inconsistent Order of View Transition Events。

哪些当转场过程中的任意阶段中断时取消非需要的效果?这时候该转场协调器(Transition
Coordinator)再次上了。

转场代理

就动画控制器后,只需要在转场前安设好转场代理便能兑现动画控制器中提供的机能。转场代理的落实大简短,但是在安代理时有不少骗局,需要留意。

iOS 8的改进:UIPresentationController

iOS 8 针对分辨率日益分裂的 iOS
设备带来了初的适应性布局方案,以往略专为在 iPad 上规划之控制器也克以
iPhone
上运用了,一个生变是于视图控制器的(模态)显示过程,包括转场过程,引入了UIPresentationController恍如,该类接管了
UIViewController 的展示过程,为那提供转场和视图管理支持。当
UIViewController
modalPresentationStyle属性为.Custom时(不支持.FullScreen),我们出空子通过控制器的转场代理提供UIPresentationController的子类对
Modal
转场进行更的定制。官方对该类参与转场的流水线和利用方式来老详尽的验证:Creating
Custom Presentations。

UIPresentationController仿佛主要让 Modal 转场带来了以下几点变化:

如上变化着第1碰 iOS 7 中也克完成,3和4凡是 iOS 8
带来的初特点,只有第2点才真正解决了 iOS 7 中之痛点。在 iOS 7
中定制外观时,动画控制器需要负责管理额外添加的底视图,UIPresentationController接近将该功能剥离了出独立承担,其提供了之类的法子与转场,对转场过程实现了逾周密的主宰,从命名就好看看与动画片控制器里之animateTransition:的关系:

func presentationTransitionWillBegin()
func presentationTransitionDidEnd(_ completed: Bool)
func dismissalTransitionWillBegin()
func dismissalTransitionDidEnd(_ completed: Bool)

除了
presentingView,UIPresentationController看似具有转场过程遭到剩下的角色:

//指定初始化方法。
init(presentedViewController presentedViewController: UIViewController, presentingViewController presentingViewController: UIViewController)
var presentingViewController: UIViewController { get }
var presentedViewController: UIViewController { get }
var containerView: UIView? { get }
//提供给动画控制器使用的视图,默认返回 presentedVC.view,通过重写该方法返回其他视图,但一定要是 presentedVC.view 的上层视图。
func presentedView() -> UIView?     

没有 presentingView 是因为 Custom 模式下 presentingView 不受
containerView 管理,UIPresentationController仿佛并无改动就一点。iOS 8
扩充了转场环境协议,可以透过viewForKey:有利得转场的视图,而拖欠方法以
Modal 转场中获得的凡presentedView()归来的视图。因此我们可以子类中将
presentedView 包装在任何视图后重新写该方法返回包装后底视图当做
presentedView 在动画控制器中利用。

接下来,我用UIPresentationController子类实现上一致节省「Modal
转场实践」里之功力,presentingView 和 presentedView
的动画由动画控制器负责,剩下的事务可交给我们实现的子类来好。

涉足角色且备好了,但发生只问题,无法直接看动画控制器,不掌握转场的持续时间,怎么跟转场过程同步?这时候前面提到的用处大少之转场协调器(Transition
Coordinator)将在此处派上用场。该目标可是经 UIViewController
transitionCoordinator()方赢得,这是 iOS 7 为从定义转场新增的
API,该办法就当控制器处于转场过程中才回一个及当下转场有关的有效对象,其他时候回来
nil。

转场协调器遵守<UIViewControllerTransitionCoordinator>商,它含有以下几独措施:

//与动画控制器中的转场动画同步,执行其他动画
animateAlongsideTransition:completion:
//与动画控制器中的转场动画同步,在指定的视图内执行动画
animateAlongsideTransitionInView:animation:completion:

由转场协调器的这种特点,动画的同台问题化解了。

class OverlayPresentationController: UIPresentationController {
    let dimmingView = UIView()

    //Presentation 转场开始前该方法被调用。
    override func presentationTransitionWillBegin() {
        self.containerView?.addSubview(dimmingView)

        let initialWidth = containerView!.frame.width*2/3, initialHeight = containerView!.frame.height*2/3
        self.dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
        self.dimmingView.center = containerView!.center
        self.dimmingView.bounds = CGRect(x: 0, y: 0, width: initialWidth , height: initialHeight)
        //使用 transitionCoordinator 与转场动画并行执行 dimmingView 的动画。
        presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in
            self.dimmingView.bounds = self.containerView!.bounds
        }, completion: nil)
    }
    //Dismissal 转场开始前该方法被调用。添加了 dimmingView 消失的动画,在上一节中并没有添加这个动画,
    //实际上由于 presentedView 的形变动画,这个动画根本不会被注意到,此处只为示范。
    override func dismissalTransitionWillBegin() {
        presentedViewController.transitionCoordinator()?.animateAlongsideTransition({ _ in
            self.dimmingView.alpha = 0.0
            }, completion: nil)
    }    
}

OverlayPresentationController恍如接手了 dimmingView
的做事晚,需要回到上亦然省OverlayAnimationController里拿干 dimmingView
的片段去,然后于 presentedVC
的转场代理属性transitioningDelegate屡遭提供此类实例就足以实现和达标一致节约一样的效能。

func presentationControllerForPresentedViewController(_ presented: UIViewController, 
                              presentingViewController presenting: UIViewController, 
                                      sourceViewController source: UIViewController) -> UIPresentationController?{
    return OverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
}

每当 iOS 7 中,Custom 模式的 Modal 转场里,presentingView
不会见叫移除,如果我们如果移除它并妥善恢复会破坏动画控制器的独立性使得第三在动画控制器无法直接动用;在
iOS 8
中,UIPresentationController化解了这点,给予了我们挑选的权,通过重新写下面的主意来决定
presentingView 是否以 presentation 转场结束后吃移除:

func shouldRemovePresentersView() -> Bool

回到 true 时,presentation 结束晚 presentingView 被移除,在 dimissal
结束晚 UIKit 会自动将 presentingView
恢复到原来的视图结构中。通过UIPresentationController的插足,Custom
模式了落实了 FullScreen 模式下之全套风味。

而恐怕会见纳闷,除了解决了 iOS 7中无法干预 presentingView
这个痛点外,还有啊说辞值得我们用UIPresentationController类似?除了能够跟动画控制器配合,UIPresentationController看似为克脱动画控制器独立工作,在转场代理里我们只是提供后者也能对
presentedView 的外观进行定制,缺点是无法控制 presentedView
的转场动画,因为及时是卡通片控制器的职责,这种情况下,presentedView
的转场动画采用的是默认的动画效果,转场协调器实现的卡通片则是行使默认的卡通片时间。

iOS 8
带来了适应性布局,<UIContentContainer>共谋用于应视图尺寸变与屏幕旋转事件,之前用于拍卖屏幕旋转的方还深受废除了。UIViewController
和 UIPresentationController 类都遵循该协议,在 Modal
转场中若提供了后世,则由后者负责前者的尺码变化及屏幕旋转,最终之布局时吧在后者里。在OverlayPresentationController中再次写以下措施来调动视图布局及对屏幕旋转:

override func containerViewWillLayoutSubviews() {
    self.dimmingView.center = self.containerView!.center
    self.dimmingView.bounds = self.containerView!.bounds

    let width = self.containerView!.frame.width * 2 / 3, height = self.containerView!.frame.height * 2 / 3
    self.presentedView()?.center = self.containerView!.center
    self.presentedView()?.bounds = CGRect(x: 0, y: 0, width: width, height: height)
}

案例剖析

动画片的持续时间一般不超过0.5秒,稍纵即没有,有时候看到一个错综复杂的转场动画也未便于掌握实现之道,我一般是通过逐帧解析的伎俩来分析实现之伎俩:开源的就是运行一下,使用系统自带的
QuickPlayer 对 iOS 设备进行录屏,再使用 QuickPlayer 打开视频,按下 cmd+T
打开剪辑功能,这时候就会查看各个一样轴了;Gif
等格式的原型动画的动图就直接用系统自带的 Preview 打开看中间帧。

子元素动画

当转场动画涉及视图中之子视图时,往往束手无策凭第三着的动画库来落实,你不能不也这种意义单独定制,神奇移动就是一个杰出的例子。神奇移动是
Keynote 中的一个动画效果,如果某个元素以连续的少数页 Keynote
同时设有,在页面切换时,该因素于达到平等页的位置走到下一样页的职,非常神奇。在转场中怎么落实者功能呢?最简便的计是截图配合移动动画:伪造好元素的视图添加到
containerView 中,从 fromView 中之岗位走至 toView 中之岗位,这中
fromView 和 toView 中的欠因素视图隐藏,等到移动了恢复 toView
中该因素的亮,并将伪造之要素视图从 containerView 中移除。

UIView 有几个convert主意用于在不同的视图之间变换坐标:

func convertPoint(_ point: CGPoint, toView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint

针对截图这个需要,iOS 7 提供了趁手的工具,UIView Snapshot API:

func snapshotViewAfterScreenUpdates(_ afterUpdates: Bool) -> UIView
//获取视图的部分内容
func resizableSnapshotViewFromRect(_ rect: CGRect, afterScreenUpdates afterUpdates: Bool, withCapInsets capInsets: UIEdgeInsets) -> UIView

afterScreenUpdates参数值为true常常,这片个措施能够强制视图立刻更新内容,同时返回更新后底视图内容。在
push 或 presentation 中,如果 toVC 是 CollectionViewController
并且需要针对 visibleCells
进行动画,此时动画控制器里是无能为力获得到的,因为这时候 collectionView
还未向数据源询问内容,执行之措施后能达到目的。UIView
layoutIfNeeded()啊能够要求当即刷新布局及同等的功效。

Mask 动画

365体育网址 4

左手的动画教程:How To Make A View Controller Transition Animation Like
in the Ping
App;右边动画的开源地址:BubbleTransition。

Mask
动画往往在视觉及教人记忆深刻,这种动画通过行使同一种植特定形状的图作为
mask 截取当前视图内容,使得当前视图只表现出 mask 图形部分的内容,在 PS
界俗称「遮罩」。UIView
有个特性maskView得就此来遮部分内容,但此间的职能并无是指向maskView的应用;CALayer
有只照应的性质mask,而 CAShapeLayer 这个子类搭配 UIBezierPath
类可以兑现各种不平整图形。这种动画一般就是是 mask + CAShapeLayer +
UIBezierPath
的组合拳搞定的,实际上实现这种圆形的形变是老大简短的,只要发表您的想象力,可以实现其他形状的形变动画。

立刻仿佛转场动画在转场过程遭到针对 toView 使用 mask
动画,不过,右边的这动画实际上并无是方的咬合来好的,它的庐山真面目是如此:

365体育网址 5

这个开发者实在是最为天才了,这个手法本身就是对 mask
概念的以,效果一流,但方也简单到怀疑。关于下 mask +
CAShapeLayer + UIBezierPath 这种措施实现 mask
动画的办法要看自己的即篇稿子。

大性能动画框架

稍许动画使用 UIView 的动画 API
难以实现,或者难以达到较好之性,又要两者皆有,幸好我们还起其他选项。StartWar 使用重复底层的
OpenGL 框架来解决性能问题跟 Objc.io 在追究转场这个话题时以 GPUImage
定制动画且是就类的金科玉律。在彼此控制器章节中涉嫌了,官方不得不针对
UIView 动画 API
实现之转场动画实施宏观的彼此控制,这吗不是绝对的,接下我们就算来挑战这个难题。

尾声:转场动画的宏图

尽管如此我莫是设计师,但还是想念在收尾之前聊一姑自己本着转场动画设计的见。动画的使确能晋级以之经验,但但限于使用了适当的动画。

除去有加载动画可以炫酷华丽极尽炫技之会从事,绝大部分之平凡操作并无抱采取过度炫酷或复杂的动画片,比如 VCTransitionsLibrary 这个库里的大多数效益。该库提供了大半上10种转场效果,从技术上讲,大部分作用都是指向
transform
进行动画,如果你针对这些感兴趣或恰好有应声上面的使用要求,可以上这些功能的落实,从代码角度看,封装技巧吧够呛值得学习,这个库房是读转场动画的极佳范例;不过由使用效能上看,这个库房提供的成效像
PPT
里提供的动画片效果同样,绝大部分还应避免在日常操作中使用。不过当开发者,我们该清楚技术实现的招数,即使这些力量并无抱当绝大部分面貌中采用。

气象转换的目的是对接至下一个景,在操作频繁的通常状况被使复杂的过场动画容易造成视觉疲劳,这种景象下使用简易的卡通片即可,实现起来非常简单,更多的行事多次是怎么把她和另外特色还好地做起来,正使 FDFullscreenPopGesture 做的那么。除了普通操作,也会见逢有些破例之观需要定制复杂的转场动画,这种复杂除了动画效果自己的错综复杂,这亟需掌握相应的动画片手段,也或涉及转场过程的相当,这要针对转场机制比较熟悉。比如 StarWars,这个转场动画在视觉及太惊艳,一上台就是获取上千鲜的厚,它产生贴合星战内涵之创意设计和惊艳的视觉呈现,以及美好之性优化,如果只要评选年度转场动画甚至是史上最佳,我会投票于她;而己于本文里实现的范例,从动画效果来讲,都是挺简单的,可以预见本文无法吸引公众的转发,压轴环节里之自定义容器控制器转场也是如此,但是后者要熟知转场机制才兑现。从当时点来拘禁,转场动画在实质上用被走向个别独极度:日常状况被之转场动画大简单易行,实现难度大没有;特定情景的转场动画可能非常复杂,不过实现难度并无克相提并论,正如我当案例分析一省里指出的几乎单案例那样。

意在本文能帮助而。

http://www.bkjia.com/IOSjc/1110870.htmlwww.bkjia.comtruehttp://www.bkjia.com/IOSjc/1110870.htmlTechArticleiOS 视图控制器转场详解,ios视图转场详解 前言
屏幕左边缘右滑返回,TabBar
滑动切换,你是否喜欢并生赖就半独操作,甚至以为 App 不…

Transition Coordinator

转场协调器(Transition
Coordinator)的出台机会不多,但可是最主要先生。Modal
转场中,UIPresentationController类只能由此转场协调器来与动画片控制器同步,并行执行其他动画片;这里她可于交互式转场结束时实行一个闭包:

func notifyWhenInteractionEndsUsingBlock(_ handler: (UIViewControllerTransitionCoordinatorContext) -> Void)

当转场由交互状态转变为无互状态(在手势交互过程遭到尽管也手势结束时),无论转场的结果是完结或受裁撤,该办法还见面给调用;得益于闭包,转场协调器可以以转场过程被的自由阶段搜集动作并于竞相中止后实施。闭包中的参数是一个遵循<UIViewControllerTransitionCoordinatorContext>情商的对象,该目标由
UIKit
提供,和前边的转场环境目标<UIViewControllerContextTransitioning>意类似,它提供了相转场的状态信息。

override func viewWillAppear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.doSomeSideEffectsAssumingViewDidAppearIsGoingToBeCalled()
    //只在处于交互转场过程中才可能取消效果。
    if let coordinator = self.transitionCoordinator() where coordinator.initiallyInteractive() == true{
        coordinator.notifyWhenInteractionEndsUsingBlock({
            interactionContext in
            if interactionContext.isCancelled(){
                self.undoSideEffects()
            }
        })
    }
}

然而相状态结束时绝不转场过程的顶峰(此后动画控制器提供的转场动画根据交互结束时之状态继续或是返回到开始状态),而是由动画控制器来终止就总体:

optional func animationEnded(_ transitionCompleted: Bool)

设实现了该措施,将在转场动画结束晚调用。

UIViewController
可以由此transitionCoordinator()收获转场协调器,该措施的文档中说除非在
Modal
转场过程中,该方法才返回一个与眼前转场相关的得力对象。实际上,NavigationController
的转场中 fromVC 和 toVC 也会回一个使得对象,TabBarController
有点新鲜,fromVC 和 toVC 在转场中归的凡 nil,但是当容器的
TabBarController 可以应用该法返回一个灵光对象。

转场协调器除了上面的星星种关键作用外,也于 iOS 8
中的适应性布局中做重要角色,可以翻<UIContentContainer>磋商被的计,其中响应尺寸以及屏幕旋转事件之艺术还蕴涵一个转场协调器对象,视图的这种变更为叫网即广义上的
transition,参数中之转场协调器也由于 UIKit
提供。这个话题有点过本文的限量,就非透了,有需要的话可以查文档和系
session。

插曲:UICollectionViewController 布局转场

面前一直没涉及这种转场方式,与三可怜主流转场不同,布局转场只针对
CollectionViewController 搭配 NavigationController
的组成,且是打算被布局,而非视图。采用这种布局转场时,NavigationController
将会见用布局变化的动画来代替 push 和 pop
的默认动画。苹果自家的相片采用被的「照片」Tab
页面下了是技能:在「年度-精选-时刻」几独时刻模式间切换时,CollectionViewController
以 push 或 pop 时极力保障在同一个要素的职又展开布局转换。

布局转场的实现比三非常主流转场要简明得几近,只待满足四独规范:NavigationController

  • CollectionViewController, 且要求后者还装有同等数据源,
    并且开启useLayoutToLayoutNavigationTransitions特性为确实。

    let cvc0 = UICollectionViewController(collectionViewLayout: layout0)
    //作为 root VC 的 cvc0 的该属性必须也 false,该属性默认为 false。
    cvc0.useLayoutToLayoutNavigationTransitions = false
    let nav = UINavigationController(rootViewController: cvc0)
    //cvc0, cvc1, cvc2 必须持有同等之多寡,如果在某某时刻修改了间的一个数据源,其他的数据源必须一起,不然会拧。
    let cvc1 = UICollectionViewController(collectionViewLayout: layout1)
    cvc1.useLayoutToLayoutNavigationTransitions = true
    nav.pushViewController(cvc1, animated: true)

    let cvc2 = UICollectionViewController(collectionViewLayout: layout2)
    cvc2.useLayoutToLayoutNavigationTransitions = true
    nav.pushViewController(cvc2, animated: true)

    nav.popViewControllerAnimated(true)
    nav.popViewControllerAnimated(true)

Push
进入控制器栈后,不可知更改useLayoutToLayoutNavigationTransitions的价值,否则应用会崩溃。当
CollectionView 的数据源(section 和 cell 的数量)不完全一致时,push 和 pop
时还是会生出布局转场动画,但是当 pop 回到 rootVC
时,应用会崩溃。可否共享数据源保持同步来克服这个毛病?测试表明,这样做也许会见造成画面上的残,以及无平稳,建议不要这么做。

此外,iOS 7 支持 UICollectionView 布局之相互变(Layout Interactive
Transition),过程和控制器的交互转场(ViewController Interactive
Transition)类似,这个效果及布局转场(CollectionViewController Layout
Transition)容易混淆,前者是以自家布局转换的根基及落实了交互控制,后者是
CollectionViewController 与 NavigationController
结合后当转场的又展开布局转换。感兴趣之说话可看之功效的文档。

布局转场不支持彼此控制。Demo
地址:CollectionViewControllerLayoutTransition。

级二:交互式转场

兴奋的有来了,好信息是彼此转场的兑现难度比较你想象的如果没有。

案例剖析

动画片的持续时间一般不越0.5秒,稍纵即没有,有时候看到一个犬牙交错的转场动画也不易于了解实现的措施,我一般是经过逐帧解析的招数来分析实现之招:开源之尽管运行一下,使用系统自带的
QuickPlayer 对 iOS 设备开展录屏,再利用 QuickPlayer 打开视频,按下 cmd+T
打开剪辑功能,这时候就可知查各个一样幅了;Gif
等格式的原型动画的动图就径直以系统自带的 Preview 打开看中间帧。

子元素动画

当转场动画涉及视图中的子视图时,往往力不从心凭第三正值的动画库来促成,你不能不为这种作用单独定制,神奇移动就是一个突出的例子。神奇移动是
Keynote 中的一个动画片效果,如果某个元素以连的星星点点页 Keynote
同时设有,在页面切换时,该因素于达成平等页的位置走至下一样页的职,非常神奇。在转场中怎么落实者功能呢?最简便的计是截图配合移动动画:伪造好元素的视图添加至
containerView 中,从 fromView 中的职走至 toView 中之岗位,这间
fromView 和 toView 中的拖欠因素视图隐藏,等到移动了恢复 toView
中该因素的示,并拿冒牌之要素视图从 containerView 中移除。

UIView 有几个convert艺术用于在不同之视图之间转移坐标:

func convertPoint(_ point: CGPoint, toView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint
func convertPoint(_ point: CGPoint, fromView view: UIView?) -> CGPoint

本着截图这个要求,iOS 7 提供了趁手的家伙,UIView Snapshot API:

func snapshotViewAfterScreenUpdates(_ afterUpdates: Bool) -> UIView
//获取视图的部分内容
func resizableSnapshotViewFromRect(_ rect: CGRect, afterScreenUpdates afterUpdates: Bool, withCapInsets capInsets: UIEdgeInsets) -> UIView

afterScreenUpdates参数值为true时常,这半只道会强制视图立刻更新内容,同时返回更新后底视图内容。在
push 或 presentation 中,如果 toVC 是 CollectionViewController
并且用对 visibleCells
进行动画,此时卡通控制器里是无法得到到之,因为此时 collectionView
还无为数据源询问内容,执行此方后能够及目的。UIView
layoutIfNeeded()也会要求立即刷新布局及平的效力。

Mask 动画

365体育网址 6

左边的动画教程:How To Make A View Controller Transition Animation Like
in the Ping App;右边动画的开源地址:BubbleTransition。

Mask
动画往往以视觉及让人记忆深刻,这种动画通过使用相同种特定形状的图片作为
mask 截取当前视图内容,使得当前视图只表现有 mask 图形部分的情节,在 PS
界俗称「遮罩」。UIView
有只属性maskView可用来遮掩部分内容,但此的职能并无是对maskView的用;CALayer
有个照应之属性mask,而 CAShapeLayer 这个子类搭配 UIBezierPath
类可以实现各种非平整图形。这种动画一般就是 mask + CAShapeLayer +
UIBezierPath
的组合拳搞定的,实际上实现这种圆形的形变是坏简单的,只要发表您的想象力,可以兑现任何模样的形变动画。

立马类似转场动画在转场过程遭到针对 toView 使用 mask
动画,不过,右边的斯动画实际上并无是地方的成来完成的,它的面目是如此:

365体育网址 7

夫开发者实在是无限天才了,这个手法本身就是针对性 mask
概念的应用,效果一流,但方法可简单到怀疑。关于利用 mask +
CAShapeLayer + UIBezierPath 这种艺术实现 mask
动画的方式要看我之马上篇稿子。

赛性能动画框架

粗动画使用 UIView 的卡通 API
难以实现,或者难以达到较好之性能,又或双方均有,幸好我们尚生任何选项。StartWar 使用更底层的
OpenGL 框架来化解性能问题同 Objc.io 在探究转场这个话题时采用 GPUImage
定制动画都是即时类的旗帜。在互动控制器章节中涉嫌过,官方不得不对 UIView 动画
API
实现之转场动画实施一揽子的互相控制,这为未是绝的,接下去我们不怕来挑战这个难题。

落实交互化

每当非交互转场的功底及拿之交互化需要简单个标准:

  1. 鉴于转场代理提供相互控制器,这是一个遵照<UIViewControllerInteractiveTransitioning>磋商的对象,不过系统就于包好了现的类UIPercentDrivenInteractiveTransition供应我们采用。我们无待开另外配置,仅仅在转场代理的照应措施中提供一个此类实例便能做事。另外交互控制器必须有动画控制器才会干活。

  2. 互控制器还待彼此手段之配合,最广泛的凡使用手势,或是其他事件,来让整个转场进程。

满足上述两只极异常粗略,但是挺爱犯错误。

是的地提供相互控制器

若是在转场代理中提供了交互控制器,而转场发生时连无章程来教转场进程(比如手势),转场过程用一直处于开始流无法收场,应用界面也会失掉响应:在
NavigationController 中点击 NavigationBar 也会落实 pop
返回操作,但此刻莫了相互手段的支持,转场过程卡壳;在 TabBarController
的代理里供相互控制器是同样的题目,点击 TabBar
切换页面时也从来不落实相互之间控制。因此只在委处于交互状态时才提供相互控制器,可以利用一个变量来号交互状态,该变量由交互手势来更新状态。

看 NavigationController 提供相互控制器为例:

class SDENavigationDelegate: NSObject, UINavigationControllerDelegate {
    var interactive = false
    let interactionController = UIPercentDrivenInteractiveTransition()
    ...

    func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController 
                               animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactive ? self.interactionController : nil
    }
}

TabBarController 的落实类似,Modal 转场代理分别吗 presentation 和
dismissal 提供了分别的彼此控制器,也待留意点的题目。

问题之根源是互控制的工作体制导致的,交互过程实际上是由转场环境目标<UIViewControllerContextTransitioning>来保管之,它提供了之类几单艺术来决定转场的快慢:

func updateInteractiveTransition(_ percentComplete: CGFloat)//更新转场进度,进度数值范围为0.0~1.0。
func cancelInteractiveTransition()//取消转场,转场动画从当前状态返回至转场发生前的状态。
func finishInteractiveTransition()//完成转场,转场动画从当前状态继续直至结束。

彼此控制协议<UIViewControllerInteractiveTransitioning>就发生一个必实现的方法:

func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning)

当转场代理里提供了彼此控制器后,转场开始时,该措施自动为 UIKit
调用对转场环境开展安排。

网于包好的UIPercentDrivenInteractiveTransition遇的控制转场进度的道以及转场环境目标提供的老三单方法同名,实际上只有是前者调用了后世的艺术而已。系统因为相同种植解耦的不二法门让动画控制器,交互控制器,转场环境目标互相合作,我们只是待用UIPercentDrivenInteractiveTransition的老三独同名方法来控制速度就足足了。如果你如果落实团结的竞相控制器,而休是UIPercentDrivenInteractiveTransition的子类,就需调用转场环境之老三单办法来决定速度,压轴环节我们用示范如何做。

相互之间控制器控制转场的进程就是像以动画片控制器实现之卡通制作成一统视频,我们下手势或是其他艺术来控制转场动画的播报,可以进步,后退,继续要停止。finishInteractiveTransition()措施为调用后,转场动画从目前的状态将继承拓展直到动画结束,转场完成;cancelInteractiveTransition()于调用后,转场动画从脚下的状态回拨到起状态,转场取消。

于 NavigationController 中点击 NavigationBar 的 backBarButtomItem 执行
pop 操作时,由于我们无法参与 backBarButtomItem
的里流程,就夺控制速度的一手,于是转场过程只发生一个始发,永远不会见结束。其实我们特需要来会实施上述几只措施的手法就是好针对转场动画进行控制,用户以及屏幕的相手段里,手势是贯彻此决定过程的生手段,我猜这是那个于誉为交互控制器的由。

相互之间手段之配合

脚用演示如何行使屏幕边缘滑动手势UIScreenEdgePanGestureRecognizer
NavigationController 中决定 Slide
动画控制器提供的卡通片来兑现右滑返回的作用,该手势绑定的动作方法如下:

func handleEdgePanGesture(gesture: UIScreenEdgePanGestureRecognizer){
    //根据移动距离计算交互过程的进度。
    let percent = ...
    switch gesture.state{
    case .Began:
        //转场开始前获取代理,一旦转场开始,VC 将脱离控制器栈,此后 self.navigationController 返回的是 nil。
        self.navigationDelegate = self.navigationController?.delegate as? SDENavigationDelegate
        //更新交互状态
        self.navigationDelegate?.interactive = true
        //1.交互控制器没有 start 之类的方法,当下面这行代码执行后,转场开始;
        //如果转场代理提供了交互控制器,它将从这时候开始接管转场过程。
        self.navigationController?.popViewControllerAnimated(true)
    case .Changed:
        //2.更新进度:
        self.navigationDelegate?.interactionController.updateInteractiveTransition(percent)
    case .Cancelled, .Ended:
        //3.结束转场:
        if percent > 0.5{
            //完成转场。
            self.navigationDelegate?.interactionController.finishInteractiveTransition()
        }else{
            //或者,取消转场。
            self.navigationDelegate?.interactionController.cancelInteractiveTransition()
        }
        //无论转场的结果如何,恢复为非交互状态。
        self.navigationDelegate?.interactive = false
    default: self.navigationDelegate?.interactive = false
    }
}

互动转场的流水线便是三高居数字符号的代码。不管是什么交互方式,使用什么转场方式,都是以使用即时三单艺术控制转场的进度。对此交互式转场,交互手段只是表现形式,本质是驱动转场进程。异常盼望能看出更新颖的并行手段,比如通过点击页面不同区域来决定一样法复杂的流程动画。TabBarController
的 Demo 中也促成了滑动切换 Tab 页面,代码是相仿之,就无占篇幅了;示范的
Modal
转场我没有啊的实现相互之间控制,原因也涉过了,没有比符合操作直觉的互手段,不过真要为其增长交互控制,代码和点是近似的。

转场交互化后结果发生个别种植:完成与撤销。取消后动画将会原本路回到开始状态,但一度成形了底多少怎么回复?

同等种植情景是,控制器的网性能,比如,在 TabBarController
里使用方面的方实现滑动切换 Tab
页面,中途取消的话,已经转移之selectedIndex属性该怎么回复为原值;上面的代码里,取消转场的代码执行后,self.navigationController归来的依然要是
nil,怎么为控制器回到 NavigationController
的控制器栈顶。对于这种情景,UIKit
自动为我们还原了,不欲我们担心(可能您都未曾察觉及立刻反过来事);

除此以外一种植就是是,转场发生的过程中,你或许想实现某些意义,一般是当脚的轩然大波备受实践,转场中途取消的语也许需要取消这些力量。

func viewWillAppear(_ animated: Bool)
func viewDidAppear(_ animated: Bool)
func viewWillDisappear(_ animated: Bool)
func viewDidDisappear(_ animated: Bool)

交互转场介入后,视图在这些状态中的转移变得复杂,WWDC
上苹果之工程师还表示转场过程被 view
Will相关法及Did连带法的履行各个并无克收获保证,虽然几带领大粗,但倘若您因让这些方法执行的逐一的讲话就可能要留意这点。而且,Did系道调用时连无意味转场过程着实了了。另外,fromView
和 toView
之间的即几乎种艺术的相对顺序更加混乱,具体的案例可以参见这里:The
Inconsistent Order of View Transition
Events。

怎么当转场过程遭到的妄动阶段中断时取消非待之效果?这时候该转场协调器(Transition
Coordinator)再次登场了。

相互转场的限制

比方指望转场中之卡通片能到家地为交互控制,必须满足2单隐性条件:

苟您期望打造多流动画,在某某动画结束后重新实践另外一段子动画,可以通过
UIView Block Animation 的 completion
闭包来兑现动画链,或者是经过设定动画执行的延迟时间使得不同动画错分开来,但是彼此转场不支持这简单栽样式。UIView
的 keyFrame Animation API
可以帮助你,通过在动画过程的不等时节点上加要帧动画就得兑现多流动画。我实现了一个这样的几近阶段转场动画,Demo
在是:CollectionViewAlbumTransition。

UIViewControllerTransitioningDelegate

Modal 转场的代办协议<UIViewControllerTransitioningDelegate>是 iOS 7
新增的,其也 presentation 和 dismissal
转场分别提供了动画片控制器。在「特殊的 Modal
转场」里心想事成之OverlayAnimationController恍如可同时处理 presentation 和
dismissal 转场。UIPresentationController只在 iOS
8中可用,通过available重大字可以化解 API 的本差异。 

class SDEModalTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
    func animationControllerForPresentedController(presented: UIViewController, 
                             presentingController presenting: UIViewController, 
                                     sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return OverlayAnimationController()
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return OverlayAnimationController()
    }

    @available(iOS 8.0, *)
    func presentationControllerForPresentedViewController(presented: UIViewController, 
                                presentingViewController presenting: UIViewController, 
                                        sourceViewController source: UIViewController) -> UIPresentationController? {
        return OverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
    }
}

Modal 转场的代理由 presentedVC
transitioningDelegate性能来供,这跟眼前片种植容器控制器的转场不等同,不过该属性作为代理同样是故引用,记得跟前边一样需要来高引用的变量来维护该代理,而
Modal 转场需要 presentedVC 来供转场代理的表征使得 presentedVC
自身非常适合作为团结的转场代理。另外,需要将 presentedVC
modalPresentationStyle特性设置为.Custom.FullScreen,只有及时简单种模式下才支撑由定义转场,该属性默认值为.FullScreen。自定义转场时,决定转场动画效果的modalTransitionStyle属性将受忽略。

打开转场动画的主意还是是鲜栽:在 storyboard 里安装 segue
并打开动画,但这边并无支持.Custom模式,不过还有会挽救,转场前之末段一个环节prepareForSegue:sender:办法里好动态修改modalPresentationStyle性能;或者全部于代码里安,示例如下:

let presentedVC = ...
presentedVC.transitioningDelegate = strongReferenceSDEModalTransitionDelegate
//当与 UIPresentationController 配合时该属性必须为.Custom。
presentedVC.modalPresentationStyle = .Custom/.FullScreen      
presentingVC.presentViewController(presentedVC, animated: true, completion: nil)

Demo
地址:CustomModalTransition。

最后之卷入

连通下要召开的事体就是是拿上述代码封装于转场环境协议要求落实之老三单办法里:

func updateInteractiveTransition(percentComplete: CGFloat)
func finishInteractiveTransition()
func cancelInteractiveTransition()

正使网打包的UIPercentDrivenInteractiveTransition类似就是调用了 UIKit
提供的转场环境目标里的同名方法,我实现的SDEPercentDrivenInteractiveTransition恍如为应用了同样的法调用我们贯彻之ContainerTransitionContext接近的同名方法。

引入交互控制器后底转场引用关系图:

365体育网址 8

回到SDEContainerViewController类里修改转场过程的入口处:

private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
    ...
    if containerTransitionDelegate != nil{
        let fromVC = viewControllers![fromIndex]
        let toVC = viewControllers![toIndex]
        self.containerTransitionContext = ...//利用 fromVC 和 toVC 初始化。
        //interactive 属性标记是否进入交互状态,由手势来更新该属性的状态。
        if interactive{
            priorSelectedIndex = fromIndex //备份数据,以备取消转场时使用。
            self.containerTransitionContext?.startInteractiveTranstionWith(containerTransitionDelegate!)
        }else{
            self.containerTransitionContext?.startNonInteractiveTransitionWith(containerTransitionDelegate!)
        }
    }else{/*没有提供转场代理的话,则使用最初没有动画的转场代码,或者提供默认的转场动画*/}
} 

落实手势控制的组成部分即使假设前的互相控制器章节里之那样,完整的代码请看
Demo。

顺手说生 ButtonTabButton
在相互切换页面时的渐变色动画,这里我只是随着转场的速更改了 Button
的书体颜色而已。那么当彼此结束时如何延续剩下的动画或者取消渐变色动画也,就如交互转场动画的那样。答案是CADidplayLink,前面我用她当互动取消时逆转动画,这里以了扳平的伎俩。

关于转场协调器,文档表明在转场发生常transitionCoordinator()回一个实用对象,但系统并无支持时之转场方式,测试表明在此时此刻之转场过程遭到这法返回的凡
nil,需要更写该方式来提供。该对象仅需要实现前面提到三个法子,其中在互相中止时实行绑定的闭包的不二法门好透过通告机制来促成,有硌困难的凡零星个和动画控制器同步施行动画的法子,其索要精准地以及动画控制器中之卡通保持并,这简单独艺术还设受一个遵循<UIViewControllerTransitionCoordinatorContext>说道的参数,该协议以及转场环境协议非常相像,这个目标可以由我们实现的转场环境目标来供。不过既然现在出于我们贯彻了转场环境目标,也便亮了行动画的时机,提交并行的动画片似乎并无是难题。这一部分不怕留读者来挑战了。

包裹交互控制器

UIPercentDrivenInteractiveTransition仿佛是一个系提供的竞相控制器,在转场代理的有关方里提供一个此类实例就足足了,还发生另需要的讲话可以实现该子类来成功,那这里的包装是依靠什么?系统将彼此控制器打包好了,但是彼此控制器工作还需要任何的配置。程序员向来甚困,能够活动完成的从业绝对不乐意写一行代码,写一行代码就能够搞定的从事不用写第二行,所谓丢写一行是单排。能免可知顺便把彼此控制器的布置也起包好省得写代码啊?当然好。

看好转场动画库 VCTransitionsLibrary 封装好了余动画效果,并且自动支持
pop, dismissal 和 tab change 等操作的手势交互,其手段是以转场代理里也
toVC 添加手势并绑定相应的拍卖方法。

缘何并未支持 push 和 presentation 这简单栽转场?因为 push 和 presentation
这点儿种植转场需要提供 toVC,而仓库并没 toVC
的信,这亟需用作使用者的开发者来提供;对于逆操作的 pop 和
dismiss,toVC 的音讯已在了,所以能落实活动支持。而
TabBarController 则是独不同,它是以都掌握的子 VC
之间切换,不在这问题。需要留意的是,库这样封装了相互控制器后,那么您将无法还为同样栽手势支持
push 或
presentation,要么就支持但为的转场,要么你协调实现双向的转场。当然,如果掌握
toVC 是呀像样的口舌,你可以改写这个库房让 push 和 present
得到支持。不过,对于当初始化时要配置额外信息的接近,这种简单的卷入可能不起作用。VCTransitionsLibrary 库还支持上加起定义的简化版的动画控制器和互相控制器,在包装和活之间的平衡控制得很好,代码非常值得学习。

若愿意,我们尚可以变换得更懒,不,是效率又胜似。FDFullscreenPopGesture 通过
category 的法吃所有的 UINavigationController
都支持右滑返回,而且,一行代码都并非写,这是配套的博客:一个丝滑的全屏滑动返回手势。那么为足以实现一个好像之
FullScreenTabScrollGesture 让具有的 UITabBarController
都支持滑动切换,不过,UITabBar 上之 icon 渐变动画有点辛苦,因为内部的
UITabBarItem 并非 UIView
子类,无法进行动画。WXTabBarController 这个项目完全地促成了微信界面的滑行交互与
TabBar
的渐变动画。不过,它的滑动交互并无是下转场的不二法门就的,而是采用
UIScrollView,好处是兼容性更好。兼容性这方面国内的环境比不同,iOS 9
都出来了,可能还索要相当 iOS 6,而打定义转场需要至少 iOS 7
的网。该档实现的 TabBar 渐变动画是冲 TabBar
的内部结构实时更新相关视图的 alpha 值来兑现的(不是UIView
动画),这点大珍贵,而且用 UIScrollView 还好实现自动控制 TabBar
渐变动画,相比之下,使用转场的计来实现这个效应会烦一点。

一个较好的转场方式亟待照顾更多地方的底细,NavigationController 的
NavigationBar 和 TabBarController 的 TabBar
这两头在先天上有着众多欠缺需要花还多的生命力去到,本文就未以这上面深入了,上面提及的几乎单开源项目都召开得比好,推荐学习。

UIViewControllerTransitioningDelegate

Modal 转场的代办协议<UIViewControllerTransitioningDelegate>举凡 iOS 7
新增的,其也 presentation 和 dismissal
转场分别提供了动画片控制器。在「特殊的 Modal
转场」里心想事成之OverlayAnimationController仿佛可同时处理 presentation 和
dismissal 转场。UIPresentationController只在 iOS
8中可用,通过available根本字可以解决 API 的本差异。 

class SDEModalTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {
    func animationControllerForPresentedController(presented: UIViewController, 
                             presentingController presenting: UIViewController, 
                                     sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return OverlayAnimationController()
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return OverlayAnimationController()
    }

    @available(iOS 8.0, *)
    func presentationControllerForPresentedViewController(presented: UIViewController, 
                                presentingViewController presenting: UIViewController, 
                                        sourceViewController source: UIViewController) -> UIPresentationController? {
        return OverlayPresentationController(presentedViewController: presented, presentingViewController: presenting)
    }
}

Modal 转场的代理由 presentedVC
transitioningDelegate性能来供,这跟眼前片栽容器控制器的转场不相同,不过该属性作为代理同样是死亡引用,记得跟前边一样用出大引用的变量来保护该代理,而
Modal 转场需要 presentedVC 来供转场代理的特点使得 presentedVC
自身非常适合作为团结之转场代理。另外,需要以 presentedVC
modalPresentationStyle特性设置也.Custom.FullScreen,只有及时片种模式下才支撑于定义转场,该属性默认值为.FullScreen。自定义转场时,决定转场动画效果的modalTransitionStyle属性将为忽视。

拉开转场动画的方仍然是少种:在 storyboard 里设置 segue
并拉开动画,但此连无支持.Custom模式,不过还有会弥补,转场前的最后一个环节prepareForSegue:sender:术里可以动态修改modalPresentationStyle性能;或者全体每当代码里装,示例如下:

let presentedVC = ...
presentedVC.transitioningDelegate = strongReferenceSDEModalTransitionDelegate
//当与 UIPresentationController 配合时该属性必须为.Custom。
presentedVC.modalPresentationStyle = .Custom/.FullScreen      
presentingVC.presentViewController(presentedVC, animated: true, completion: nil)

Demo 地址:CustomModalTransition。

尾声:转场动画的宏图

虽我未是设计师,但还是想当终止之前聊一聊自己本着转场动画设计的眼光。动画的施用确能提升利用的体会,但惟独限于使用了合适的动画片。

除去部分加载动画可以炫酷华丽极尽炫技之会行,绝大部分底家常操作并无符合利用过度炫酷或复杂的卡通,比如 VCTransitionsLibrary 这个库里的大部职能。该库提供了差不多上10栽转场效果,从技术上讲,大部分效能都是本着
transform
进行动画,如果你针对这些感兴趣或恰好有就点的运要求,可以学习这些功能的实现,从代码角度看,封装技巧也老值得学习,这个库房是学转场动画的极佳范例;不过自从以效果上看,这个库房提供的功效像
PPT
里提供的动画效果同样,绝大部分都应当避免以一般操作中利用。不过作为开发者,我们应当知道技术实现之招数,即使这些成效并无合乎在多方面场景中行使。

此情此景转换的目的是联网至下一个状况,在操作频繁的一般性状况被应用复杂的过场动画容易造成视觉疲劳,这种光景下下简易的卡通片即可,实现起来非常简单,更多的行事多次是怎把她和其它特色还好地构成起来,正而 FDFullscreenPopGesture 做的那么。除了普通操作,也会见碰到有与众不同之情景需要定制复杂的转场动画,这种复杂除了动画效果自己的复杂性,这要掌握相应的动画手段,也恐怕干转场过程的配合,这亟需对转场机制比较熟悉。比如 StarWars,这个转场动画在视觉及无比惊艳,一出台就是取得上千片的青睐,它发生贴合星战内涵的创意设计和惊艳的视觉呈现,以及良好之习性优化,如果如评选年度转场动画甚至是史上最佳,我会投票被她;而己当本文里实现的范例,从动画效果来讲,都是生简单的,可以预见本文无法吸引公众的转账,压轴环节里的自定义容器控制器转场也是这般,但是后者要熟知转场机制才能兑现。从即点来拘禁,转场动画在事实上行使被走向个别独最:日常状况被之转场动画大大概,实现难度大没有;特定情景的转场动画可能非常复杂,不过实现难度并无能够相提并论,正如我当案例剖析一省里指出的几个案例那样。

期待本文能拉您。

包裹交互控制器

UIPercentDrivenInteractiveTransition恍如是一个网提供的并行控制器,在转场代理的有关办法里供一个此类实例就够用了,还起另要求的口舌可兑现该子类来完成,那这里的包是赖什么?系统将彼此控制器打包好了,但是彼此控制器工作还用任何的安排。程序员向来十分劳累,能够活动完成的从业纯属不情愿写一行代码,写一行代码就会搞定的从事绝不写第二执行,所谓丢写一行是单排。能免可知顺便把彼此控制器的布为起包好省得写代码啊?当然好。

红转场动画库 VCTransitionsLibrary 封装好了多动画效果,并且自动支持
pop, dismissal 和 tab change 等操作的手势交互,其手段是于转场代理里也
toVC 添加手势并绑定相应的拍卖措施。

为何无支持 push 和 presentation 这简单种植转场?因为 push 和 presentation
这点儿种转场需要提供 toVC,而储藏室并没 toVC
的音信,这得当作使用者的开发者来供;对于逆操作的 pop 和
dismiss,toVC 的消息已经存在了,所以能够落实机关支持。而
TabBarController 则是独不等,它是当已清楚之子 VC
之间切换,不有这个题材。需要注意的是,库这样封装了彼此控制器后,那么你拿无法再受相同种植手势支持
push 或
presentation,要么就支持就为的转场,要么你自己实现双向的转场。当然,如果知道
toVC 是啊像样的说话,你得改写这个库房让 push 和 present
得到支持。不过,对于在初始化时索要配置额外信息的类,这种简易的卷入可能不起作用。VCTransitionsLibrary 库还支持上加起定义的简化版的动画片控制器和相控制器,在包装和活之间的平衡控制得很好,代码非常值得学习。

一旦肯,我们尚得换得更懒,不,是效率又强。FDFullscreenPopGesture 通过
category 的道给具有的 UINavigationController
都支持右滑返回,而且,一行代码都休想写,这是配套的博客:一个丝滑的全屏滑动返回手势。那么也得以兑现一个类的
FullScreenTabScrollGesture 让所有的 UITabBarController
都支持滑动切换,不过,UITabBar 上之 icon 渐变动画有点累,因为内部的
UITabBarItem 并非 UIView
子类,无法展开动画。WXTabBarController 这个类别完全地实现了微信界面的滑行交互与
TabBar
的渐变动画。不过,它的滑交互并无是下转场的章程成功的,而是以
UIScrollView,好处是兼容性更好。兼容性这点国内的环境比不同,iOS 9
都出去了,可能还亟需配合 iOS 6,而自从定义转场需要至少 iOS 7
的系。该种落实之 TabBar 渐变动画是因 TabBar
的内部结构实时更新相关视图的 alpha 值来促成之(不是UIView
动画),这点老可贵,而且采用 UIScrollView 还足以兑现活动控制 TabBar
渐变动画,相比之下,使用转场的点子来落实之意义会烦一点。

一个于好之转场方式欲照顾更多点的细节,NavigationController 的
NavigationBar 和 TabBarController 的 TabBar
这两边在先天上有着广大供不应求需要花费还多之肥力去完善,本文就不在马上方面深入了,上面提及的几只开源项目还开得较好,推荐学习。

动画片控制器实现

转场 API
是商的功利是无限制具体的类似,只要对象实现该谋就会参与转场过程,这为带来另外一个利:封装好复用,尽管三可怜转场代理协议的计不尽相同,但她返回的卡通控制器遵守的凡同一个商谈,因此可将动画控制器封装作为第三着动画控制器在其他控制器的转场过程遭到采取。

其三种植转场方式都生同样对可接之转场操作,你可为了各国一样栽操作实现独立的动画片控制器,也可以兑现通用的卡通控制器。处于篇幅的设想,本文示范一个比较简单的
Slide 动画控制器:Slide left and
right,而且该动画控制器在三种转场方式受凡通用的,不必修改就足以一直以工程被利用。效果示意图:

365体育网址 9

以交互式转场章节里我们用于是基础及落实文章开始提到的片栽功效:NavigationController
右滑返回 和 TabBarController
滑动切换。尽管针对动画片控制器来说,转场方式并无紧要,可以本着 fromView 和
toView 进行其他动画,但地方的卡通片及 Modal
转场风格及略不配,主要动画的矛头不对,不过我于此 Slide
动画控制器里为 Modal 转场适配了与体系的作风看似之竖直移动动画效果;另外
Modal
转场并没比符合操作直觉的互手段,而且与前边两栽容器控制器的转场在体制及有些不同,所以自己以为
Modal 转场示范另外一个动画。

当转场中操作是可逆的,返回操作时之卡通应该吗是逆向的。对这个,Slide
动画控制器需要对转场的操作类型对动画的可行性进行调。Swift 中 enum
的关联值可以用作有限数据类型的集合体,在这种景象下最方便。设定转场类型:

enum SDETransitionType{
    //UINavigationControllerOperation 是枚举类型,有 None, Push, Pop 三种值。
    case NavigationTransition(UINavigationControllerOperation) 
    case TabTransition(TabOperationDirection)
    case ModalTransition(ModalOperation)
}

enum TabOperationDirection{
    case Left, Right
}

enum ModalOperation{
    case Presentation, Dismissal
}

应用示例:在 TabBarController 中切换到左手的页面。

let transitionType = SDETransitionType.TabTransition(.Left)

Slide 动画控制器的核心代码:

class SlideAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    init(type: SDETransitionType) {...}

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        ...
         //1
        containerView.addSubview(toView)

        //计算位移 transform,NavigationVC 和 TabBarVC 在水平方向进行动画,Modal 转场在竖直方向进行动画。
        var toViewTransform = ...
        var fromViewTransform = ...
        toView.transform = toViewTransform

        //根据协议中的方法获取动画的时间。
        let duration = self.transitionDuration(transitionContext)
        UIView.animateWithDuration(duration, animations: {
            fromView.transform = fromViewTransform
            toView.transform = CGAffineTransformIdentity
            }, completion: { _ in
                //考虑到转场中途可能取消的情况,转场结束后,恢复视图状态。
                fromView.transform = CGAffineTransformIdentity
                toView.transform = CGAffineTransformIdentity
                //2
                let isCancelled = transitionContext.transitionWasCancelled()
                transitionContext.completeTransition(!isCancelled)
        })
    }
}

留神点的代码来2处标记,是动画片控制器必须完成的:

  1. 拿 toView 添加到容器视图中,使得 toView 在屏幕及亮( Modal
    转场中斯点多少有例外,下一样节约细述);
  2. 然地了转场过程。转场的结果发生少数种:完成或收回。非交互转场的结果只是发生得同样种植情况,不过交互式转场需要考虑取消的景况。如何了在转场的快,通过transitionWasCancelled()道来赢得转场的状态,使用completeTransition:来完成或撤销转场。

实质上,这里示范的简短的转场动画以及那些老复杂的转场动画在转场的有些如举行的事体还是端提到的当即片沾,它们的区分主要在于动画的片。

转场结束晚,fromView 会从视图结构被移除,UIKit
自动为我们开了当时行,你也可手动处理提前拿 fromView
移除,这了在于你。UIView的近乎方式transitionFromView:toView:duration:options:completion:啊克做一样的从业,使用下的代码替换上面的代码,甚至无需得到
containerView 以及手动添加 toView 就会落实一个像样之转场动画:

UIView.transitionFromView(fromView, toView: toView, duration: durantion, options: .TransitionCurlDown, completion: { _ in
    let isCancelled = transitionContext.transitionWasCancelled()
    transitionContext.completeTransition(!isCancelled)
})

等同:非交互转场

本条阶段要做简单件事,提供转场代理并由代理提供动画控制器。在转场代理协议里动画控制器和互动控制器都是可选实现之,没有兑现或者返回
nil
的语虽运用默认的转场效果。动画控制器是显现转场效果的基本部分,代理部分非常简单,我们先行将定动画控制器吧。

落实分析

既是这个自定义容器控制器和 UITabBarController
行为看似,我就是实现了扳平效仿类似的 API:viewControllers数组是容器 VC
维护的子 VC 数组,初始化时提供要来得的子
VC,更改selectedIndex的价就是只是超过反至对应之子视图。利用 Swift
的性质观察器实现修改selectedIndex常自动执行子控制器转场。下面是实现子
VC 转场的核心代码,转场结束晚随系统的老办法将 fromView 移除:

class SDEContainerViewController: UIViewController{
    ...
    //发生转场的容器视图,是 root view 的子视图。
    private let privateContainerView = UIView()
    var selectedIndex: Int = NSNotFound{
        willSet{
            transitionViewControllerFromIndex(selectedIndex, toIndex: newValue)
        }
    }
    //实现 selectedVC 转场:
    private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
        //添加 toVC 和 toView
        let newSelectedVC = viewControllers![toIndex]
        self.addChildViewController(newSelectedVC)
        self.privateContainerView.addSubview(newSelectedVC.view)
        newSelectedVC.didMoveToParentViewController(self)

        UIView.animateWithDuration(transitionDuration, animations: {
            /*转场动画*/
            }, completion: { finished in
                //移除 fromVC 和 fromView。
                let priorSelectedVC = viewControllers![fromIndex]
                priorSelectedVC.willMoveToParentViewController(nil)
                priorSelectedVC.view.removeFromSuperview()
                priorSelectedVC.removeFromParentViewController()
        })
    }
}

兑现转场就是这般十几执代码而已,其他容器 VC
转场过程做了看似之事体。回忆下我们于动画控制器里开的政工,实际上只是上面代码中之等同局部。转场协议就套
API
将此过程分割为五只零部件,这套复杂的组织带来了而是高度自定义之卡通片效果与彼此控制。我们温习下转场协议,来看望哪当既有的转场协议框架下实现由定义容器控制器的转场动画与互动控制:

  1. 转场代理:既有的转场代理协议并无一直支持我们这种转场方式,没提到,我们从定义一仿代理协议来供动画控制器和相互控制器;
  2. 动画控制器:动画控制器是只是复用的,这里用动画控制器章节封装的 Slide
    动画控制器,可以拿来直接动用要非用修改;
  3. 相互控制器:官方封装了一个成的互相控制器类,但以此看似是和 UIKit
    提供的转场环境目标配合下的,而这里的转场显然要我们来提供转场环境目标,因此UIPercentDrivenInteractiveTransition束手无策以此以,需要我们来实现这个协议;
  4. 转场环境:在官方支持之转场方式受,转场环境是由 UIKit
    主动提供给咱们的,既然现在的转场方式不是官支持之,显然要我们团结一心提供这目标为供动画控制器和相控制器采用;
  5. 转场协调器:在前方的回中自己关系了,转场协调器(Transition
    Coordinator)的运状况有限而首要,也是出于网提供,我们呢得更写相关方法来提供。这个片自养读者当作是本文的如出一辙志作业吧。

脚我们来以方面的十几推行代码(不包实际的动画代码)使用协议封装成本文前半组成部分里熟悉的旗帜。

动画片控制器实现

转场 API
是协商的利是匪限制具体的好像,只要对象实现该协议就会参与转场过程,这为牵动另外一个补:封装好复用,尽管三坏转场代理协议的点子不尽相同,但她返回的动画控制器遵守的凡和一个商事,因此可将动画片控制器封装作为第三正在动画控制器在另控制器的转场过程中使。

其三栽转场方式还生同样对可接之转场操作,你得为了各国一样栽操作实现独立的动画片控制器,也得兑现通用的卡通控制器。处于篇幅的设想,本文示范一个比较简单的
Slide 动画控制器:Slide left and
right,而且该动画控制器在三种植转场方式受到凡通用的,不必修改就可以直接以工程被使用。效果示意图:

365体育网址 10

于交互式转场章节里我们用于此基础及贯彻文章开始提到的一定量栽意义:NavigationController
右滑返回 和 TabBarController
滑动切换。尽管针对动画片控制器来说,转场方式并无紧要,可以本着 fromView 和
toView 进行任何动画,但上面的卡通片与 Modal
转场风格上多少不配,主要动画的势头不对,不过我于这个 Slide
动画控制器里吗 Modal 转场适配了跟网的作风类似之竖直移动动画效果;另外
Modal
转场并不曾比可操作直觉的相手段,而且与前边两种容器控制器的转场在机制及稍稍不同,所以自己以为
Modal 转场示范另外一个卡通。

于转场中操作是可逆的,返回操作时之卡通应该也是逆向的。对是,Slide
动画控制器需要对转场的操作类型对动画片的势头进行调。Swift 中 enum
的关联值可以当有限数据类型的集合体,在这种场面下最方便。设定转场类型:

enum SDETransitionType{
    //UINavigationControllerOperation 是枚举类型,有 None, Push, Pop 三种值。
    case NavigationTransition(UINavigationControllerOperation) 
    case TabTransition(TabOperationDirection)
    case ModalTransition(ModalOperation)
}

enum TabOperationDirection{
    case Left, Right
}

enum ModalOperation{
    case Presentation, Dismissal
}

用示例:在 TabBarController 中切换到左手的页面。

let transitionType = SDETransitionType.TabTransition(.Left)

Slide 动画控制器的主干代码:

class SlideAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    init(type: SDETransitionType) {...}

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        ...
         //1
        containerView.addSubview(toView)

        //计算位移 transform,NavigationVC 和 TabBarVC 在水平方向进行动画,Modal 转场在竖直方向进行动画。
        var toViewTransform = ...
        var fromViewTransform = ...
        toView.transform = toViewTransform

        //根据协议中的方法获取动画的时间。
        let duration = self.transitionDuration(transitionContext)
        UIView.animateWithDuration(duration, animations: {
            fromView.transform = fromViewTransform
            toView.transform = CGAffineTransformIdentity
            }, completion: { _ in
                //考虑到转场中途可能取消的情况,转场结束后,恢复视图状态。
                fromView.transform = CGAffineTransformIdentity
                toView.transform = CGAffineTransformIdentity
                //2
                let isCancelled = transitionContext.transitionWasCancelled()
                transitionContext.completeTransition(!isCancelled)
        })
    }
}

小心点的代码来2处标记,是动画片控制器必须形成的:

实际,这里示范的概括的转场动画以及那些可怜复杂的转场动画在转场的组成部分要召开的事情还是点提到的这半碰,它们的界别主要在于动画的有的。

转场结束后,fromView 会从视图结构被移除,UIKit
自动为我们举行了即从,你也可手动处理提前将 fromView
移除,这统统在于你。UIView的近乎方式transitionFromView:toView:duration:options:completion:呢能召开同的行,使用下的代码替换上面的代码,甚至无待取
containerView 以及手动添加 toView 就能够实现一个类之转场动画:

UIView.transitionFromView(fromView, toView: toView, duration: durantion, options: .TransitionCurlDown, completion: { _ in
    let isCancelled = transitionContext.transitionWasCancelled()
    transitionContext.completeTransition(!isCancelled)
})

特殊的 Modal 转场

彼此控制

互动控制器的协商<UIViewControllerInteractiveTransitioning>单独要求落实一个必须的365体育网址方:

func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning)

据悉文档的叙说,该措施用于配置和启动交互转场。我们眼前使用的UIPercentDrivenInteractiveTransition接近提供的换代快的方式只有是调用了转场环境目标的相关方法。所以,是转场环境目标同交互控制器把水污染活累在干了,我们的落实还是涵养这种干好了。正使前方说之,「交互手段只是表现形式,本质是使转场进程」,让咱回到转场环境目标里实现对动画片进度的控制吧。

怎决定动画的速度?这个题目之精神是怎么落实对 UIView
的 animateWithDuration:animations:completion:当时好像方式变的卡通的操纵。能够控制也?能。

末段之卷入

联网下去要召开的事体虽是用上述代码封装在转场环境协议要求落实之老三个方式里:

func updateInteractiveTransition(percentComplete: CGFloat)
func finishInteractiveTransition()
func cancelInteractiveTransition()

凑巧而系打包的UIPercentDrivenInteractiveTransition类就是调用了 UIKit
提供的转场环境目标里之同名方法,我实现的SDEPercentDrivenInteractiveTransition看似为应用了同的措施调用我们贯彻的ContainerTransitionContext仿佛的同名方法。

引入交互控制器后的转场引用关系图:

365体育网址 11

回到SDEContainerViewController类里修改转场过程的入口处:

private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
    ...
    if containerTransitionDelegate != nil{
        let fromVC = viewControllers![fromIndex]
        let toVC = viewControllers![toIndex]
        self.containerTransitionContext = ...//利用 fromVC 和 toVC 初始化。
        //interactive 属性标记是否进入交互状态,由手势来更新该属性的状态。
        if interactive{
            priorSelectedIndex = fromIndex //备份数据,以备取消转场时使用。
            self.containerTransitionContext?.startInteractiveTranstionWith(containerTransitionDelegate!)
        }else{
            self.containerTransitionContext?.startNonInteractiveTransitionWith(containerTransitionDelegate!)
        }
    }else{/*没有提供转场代理的话,则使用最初没有动画的转场代码,或者提供默认的转场动画*/}
} 

兑现手势控制的有即使假设前的彼此控制器章节里之那么,完整的代码请看
Demo。

顺便说生 ButtonTabButton
在相互切换页面时的渐变色动画,这里自己只是随着转场的进度更改了 Button
的字颜色而已。那么当彼此结束时如何延续剩下的卡通或者取消渐变色动画也,就比如交互转场动画的那么。答案是CADidplayLink,前面我用她于彼此取消时逆转动画,这里用了千篇一律的手段。

至于转场协调器,文档表明在转场发生时transitionCoordinator()回到一个行之有效对象,但系统并无支持即的转场方式,测试表明在时下之转场过程中这办法返回的凡
nil,需要更写该法来供。该目标仅需要实现前面提到三个方式,其中以竞相中止时实行绑定的闭包的方法可以经过通告机制来兑现,有接触困难的凡有限独与动画控制器同步执行动画的点子,其索要精准地及动画片控制器中之卡通保持同步,这片独道还设经受一个遵照<UIViewControllerTransitionCoordinatorContext>协和的参数,该协议及转场环境协议非常相像,这个目标好由我们实现的转场环境目标来供。不过既然现在由咱们贯彻了转场环境目标,也便清楚了实行动画的机,提交并行的动画片似乎并无是难题。这一部分哪怕留下读者来挑战了。

等级二:交互式转场

激动的一部分来了,好信息是并行转场的贯彻难度比你想象的若小。

Transition 解释

前言里由作为上分解了转场,那在转场时有了呀?下图是于 WWDC 2013
Session 218 整理的,解释了转场时视图控制器和夫对应之视图在结构上的转变:

365体育网址 12

转场过程遭到,作为容器的父 VC 维护在多单子
VC,但于视图结构及,只保留一个子 VC 的视图,所以转场的本来面目是产同样情景(子
VC)的视图替换当前光景(子 VC)的视图以及相应的控制器(子
VC)的替换,表现吧当前视图消失和下一视图出现,基于这开展动画,动画的方式充分多,所以限制最终呈现的功用就算惟有你的想象力了。图中之
Parent VC 可替换为 UIViewController, UITabbarController 或
UINavigationController 中的旁一样种植。

目前为止,官方支持以下几栽艺术的自定义转场:

  1. 在 UINavigationController 中 push 和 pop;
  2. 在 UITabBarController 中切换 Tab;
  3. Modal 转场:presentation 和
    dismissal,俗称视图控制器的模态显示与消退,仅限于modalPresentationStyle性能为
    UIModalPresentationFullScreen 或 UIModalPresentationCustom
    这半种模式;
  4. UICollectionViewController 的布局转场:UICollectionViewController 与
    UINavigationController 结合的转场方式,实现好简短。

官方的支撑包含了 iOS
中之多数转场方式,还有一样种植起定义容器中之转场并不曾收获系统的直白支持,不过借助协议这种活的措施,我们仍能够实现对从定义容器控制器转场的定制,在压轴环节我们将促成即时一点。

iOS 7 以商事的法开了从定义转场的
API,协议的利益是不再拘泥于实际的某部类,只要是遵循该谋的对象还能够与转场,非常灵活。转场协议由5栽协议组成,在实质上中独需要我们提供其中的有限单或三单就能兑现绝大部分底转场动画:

1.转场代理(Transition Delegate):

由定义转场的第一步就是是供转场代理,告诉系统应用我们提供的代办要未是系的默认代理来实行转场。有如下三种植转场代理,对诺者三栽档次的转场:

<UINavigationControllerDelegate> //UINavigationController 的 delegate 属性遵守该协议。
<UITabBarControllerDelegate> //UITabBarController 的 delegate 属性遵守该协议。
<UIViewControllerTransitioningDelegate> //UIViewController 的 transitioningDelegate 属性遵守该协议。

这里除了<UIViewControllerTransitioningDelegate>大凡
iOS 7 新增的商事,其他两种植在 iOS 2 里就是在了,在 iOS 7
时扩充了及时简单种植协议来支持从定义转场。

转场发生时,UIKit
将要求转场代理将提供转场动画的主干部件:动画控制器和彼此控制器(可选的);由咱们兑现。

2.动画控制器(Animation Controller):

绝根本之有,负责上加视图以及实施动画;遵守<UIViewControllerAnimatedTransitioning>协商;由我们兑现。

3.相互控制器(Interaction Controller):

由此相互手段,通常是手势来让动画控制器实现之卡通片,使得用户能够决定总体经过;遵守<UIViewControllerInteractiveTransitioning>商事;系统已打包好现成的类似供应我们下。

4.转场环境(Transition Context):

提供转场中要之数;遵守<UIViewControllerContextTransitioning>合计;由
UIKit 在转场开始前别并提供于咱提交的卡通控制器和互动控制器采用。

5.转场协调器(Transition Coordinator):

但在转场动画发生的同时并行执行其他的卡通,其用意与其说协调不如说辅助,主要以
Modal
转场和彼此转场取消时采取,其他时候大少用到;遵守<UIViewControllerTransitionCoordinator>商讨;由
UIKit 在转场时别,UIViewController 在 iOS 7
中新增了方transitionCoordinator()返一个守该协议的对象,且该方法才以拖欠控制器处于转场过程遭到才回来一个此类对象,不参与转场时回来
nil。

小结下,5只商量才待我们担心3独;实现一个低于限度可用之转场动画,我们才待提供地方五单零件里之有数只:转场代理及卡通控制器即可,还有一个转场环境是必要的,不过就是因为网提供;当越来越落实互动转场时,还需要我们提供相互控制器,也发生成的类供应我们下。

Modal 转场实践

UIKit 已经为 Modal 转场实现了又功能,当 UIViewController
modalPresentationStyle属性为.Custom.FullScreen时,我们便发出空子定制转场效果,此时modalTransitionStyle点名的转场动画将见面被忽略。

Modal 转场开放由定义功能后最为令人谢兴趣的凡定制 presentedView
的尺寸,下面来咱们来兑现一个带暗色调背景的有点窗口功能。Demo
地址:CustomModalTransition。

365体育网址 13

是因为需要保持 presentingView 可见,这里的 Modal 转场应该利用
UIModalPresentationCustom 模式,此时 presentedVC
modalPresentationStyle属于性值应安装也.Custom。而且和容器 VC
的转场的代理由容器 VC 自身的代办提供不同,Modal 转场的代理由 presentedVC
提供。动画控制器的主干代码:

class OverlayAnimationController: NSobject, UIViewControllerAnimatedTransitioning{
    ... 
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {            
        ...
        //不像容器 VC 转场里需要额外的变量来标记操作类型,UIViewController 自身就有方法跟踪 Modal 状态。
        //处理 Presentation 转场:
        if toVC.isBeingPresented(){
            //1
            containerView.addSubview(toView)
            //在 presentedView 后面添加暗背景视图 dimmingView,注意两者在 containerView 中的位置。
            let dimmingView = UIView()
            containerView.insertSubview(dimmingView, belowSubview: toView)

            //设置 presentedView 和 暗背景视图 dimmingView 的初始位置和尺寸。
            let toViewWidth = containerView.frame.width * 2 / 3
            let toViewHeight = containerView.frame.height * 2 / 3
            toView.center = containerView.center
            toView.bounds = CGRect(x: 0, y: 0, width: 1, height: toViewHeight)

            dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
            dimmingView.center = containerView.center
            dimmingView.bounds = CGRect(x: 0, y: 0, width: toViewWidth, height: toViewHeight)

            //实现出现时的尺寸变化的动画:
            UIView.animateWithDuration(duration, delay: 0, options: .CurveEaseInOut, animations: {
                toView.bounds = CGRect(x: 0, y: 0, width: toViewWidth, height: toViewHeight)
                dimmingView.bounds = containerView.bounds
                }, completion: {_ in
                    //2
                    let isCancelled = transitionContext.transitionWasCancelled()
                    transitionContext.completeTransition(!isCancelled)
            })
        }
        //处理 Dismissal 转场,按照上一小节的结论,.Custom 模式下不要将 toView 添加到 containerView,省去了上面标记1处的操作。
        if fromVC.isBeingDismissed(){
            let fromViewHeight = fromView.frame.height
            UIView.animateWithDuration(duration, animations: {
                fromView.bounds = CGRect(x: 0, y: 0, width: 1, height: fromViewHeight)
                }, completion: { _ in
                    //2
                    let isCancelled = transitionContext.transitionWasCancelled()
                    transitionContext.completeTransition(!isCancelled)
            })
        }
    }
}

插曲:UICollectionViewController 布局转场

前方一直未曾关系这种转场方式,与三挺主流转场不同,布局转场只对
CollectionViewController 搭配 NavigationController
的做,且是图为布局,而非视图。采用这种布局转场时,NavigationController
将会为此布局变化之卡通片来代表 push 和 pop
的默认动画。苹果自家的照采用中之「照片」Tab
页面下了这技术:在「年度-精选-时刻」几个日子模式间切换时,CollectionViewController
在 push 或 pop 时卖力保障于同一个要素的职位而展开布局转换。

布局转场的实现比三特别主流转场要简明得几近,只需要满足四独标准化:NavigationController

  • CollectionViewController, 且要求后者还负有相同数据源,
    并且开启useLayoutToLayoutNavigationTransitions属性也确实。

    let cvc0 = UICollectionViewController(collectionViewLayout: layout0)
    //作为 root VC 的 cvc0 的该属性必须也 false,该属性默认为 false。
    cvc0.useLayoutToLayoutNavigationTransitions = false
    let nav = UINavigationController(rootViewController: cvc0)
    //cvc0, cvc1, cvc2 必须拥有同等之数,如果以某时刻修改了里面的一个数据源,其他的数据源必须联合,不然会拧。
    let cvc1 = UICollectionViewController(collectionViewLayout: layout1)
    cvc1.useLayoutToLayoutNavigationTransitions = true
    nav.pushViewController(cvc1, animated: true)

    let cvc2 = UICollectionViewController(collectionViewLayout: layout2)
    cvc2.useLayoutToLayoutNavigationTransitions = true
    nav.pushViewController(cvc2, animated: true)

    nav.popViewControllerAnimated(true)
    nav.popViewControllerAnimated(true)

Push
进入控制器栈后,不可知更改useLayoutToLayoutNavigationTransitions的值,否则应用会崩溃。当
CollectionView 的数据源(section 和 cell 的数码)不完全一致时,push 和 pop
时还会发出布局转场动画,但是当 pop 回到 rootVC
时,应用会崩溃。可否共享数据源保持同来摆平这个毛病?测试表明,这样做可能会见导致画面上之欠缺,以及非安静,建议并非这样做。

另外,iOS 7 支持 UICollectionView 布局的交互变(Layout Interactive
Transition),过程以及控制器的相转场(ViewController Interactive
Transition)类似,这个作用及布局转场(CollectionViewController Layout
Transition)容易混淆视听,前者是于自身布局转换的功底及实现了互控制,后者是
CollectionViewController 与 NavigationController
结合后在转场的又进行布局转换。感兴趣之话语可关押这效应的文档。

布局转场不支持彼此控制。Demo
地址:CollectionViewControllerLayoutTransition。

转场代理

完了动画控制器后,只待以转场前安装好转场代理便能够实现动画控制器中提供的机能。转场代理的贯彻深简单,但是当设置代理时有不少陷阱,需要专注。

由定义容器控制器转场

压轴环节我们用贯彻如此一个效能:

365体育网址 14
365体育网址 15

Demo
地址:CustomContainerVCTransition。

解析一下思路,这个控制器和 UITabBarController 在作为及比较一般,只是
TabBar 由下跑至了上面。我们可动用 UITabBarController
子类,然后做一个伪 TabBar 放在顶部,原来的 TabBar
则藏身,行为及了一致,使用 UITabBarController
子类的补是好减轻实现转场的当,不过,有时候这样的子类不是公想如果的,UIViewController
子类能够提供再多之自由度,好吧,一个全然模拟 UITabBarController 行为的
UIViewController
子类,实际上自己无想到非得这般做的故,但我想一定起要定制自己之容器控制器的场面,这多亏本节要追究的。Objc.io
也讨论了本条话题,文章的终极把落实互动控制作为作业留了下去。珠玉在前,我虽立在大牛的肩上继续这话题吧。Objc.io
的当即篇稿子写得比早采用了 Objective-C
语言,如果要是读者先去念就首文章还累读本节的情节,难免割裂,所以本节或开始讨论这话题吧,最终效果使上面所示,在打定义之器皿控制器中实现互动控制切换子视图,也得以通过填写了
UIButton 的 ButtonTabBar 来兑现 TabBar 一样行为的 Tab
切换,在通过手势切换页面时 ButtonTabBar 会实现渐变色动画。ButtonTabBar
有老怪扩展性,改造或替换为其它视图还是产生好多利用场景的。

Modal 转场的出入

Modal 转场中需要做的政工与一定量种植容器 VC 的转场一样,但当细节上小区别。

365体育网址 16

UINavigationController 和 UITabBarController 这简单个容器 VC
的根视图在屏幕上是不可见的(或者说是透明底),可见的单纯是内嵌在及时两者中之子
VC 中的视图,转场是起子 VC 的视图转换到另外一个子 VC
的视图,其根视图并未涉足转场;而 Modal 转场,以 presentation 为条例,是自
presentingView 转换到 presentedView,根视图 presentingView 也尽管是
fromView 参与了转场。而且 NavigationController 和 TabBarController
转场中之 containerView 也无须就两头的根视图。

Modal 转场与区区种容器 VC 的转场的另外一个不一是:Modal 转场结束晚
presentingView 可能仍然可见,UIModalPresentationPageSheet
模式就是是这般。这种不同造成了 Modal 转场和容器 VC 的转场对 fromView
的处理差异:容器 VC 的转场结束后 fromView
会被主动移有视图结构,这是可预见的结果,我们啊可以于转场结束前手动移除;而
Modal 转场中,presentation 结束后 presentingView(fromView)
并未主动给从视图结构被移除。准确的话,是 UIModalPresentationCustom
这种模式下之 Modal 转场结束时 fromView
并未从视图结构被移除;UIModalPresentationFullScreen 模式的 Modal
转场结束晚 fromView 依然主动给从视图结构面临改换除。这种区别导致在处理
dismissal
转场的时刻很爱并发问题,没有意识及这个不同点的讲话有错时就是会见毫不头绪。下面来探视
dismissal 转场时的场景。

ContainerView 于转场期间作为 fromView 和 toView
的父视图。三栽转场过程被的 containerView 是 UIView
的个人子类,不过我们并不需要关心 containerView 具体是呀。在 dismissal
转场中:

于 Custom 模式,我们可参见外转场里之拍卖规则来打理:presentation
转场结束晚主动将 fromView(presentingView)
移有其的视图结构,并为此一个变量来维护 presentingView 的父视图,以便在
dismissal 转场中还原;在 dismissal 转场中,presentingView 的角色由原本的
fromView 切换成了
toView,我们更将那个又恢复它原本的视图结构中。测试表明这样做是实用的。但是这样一来,在促成达标,需要以转场代理中保障一个卡通控制器又是动画控制器要维护
presentingView
的父视图,第三正在的动画控制器必须也这改造。显然,这样的代价是力不从心经受之。

小结:经过地方的品味,建议是,不要干预官方对 Modal
转场的处理,我们失去适应其。在 Custom 模式下,由于 presentingView 不给
containerView 管理,在 dismissal 转场中不用像另的转场那样以
toView(presentingView) 加入 containerView,否则 presentingView
将消灭不见,而采用则为够呛可能假死;而于 presentation
转场中,切记不要手动将 fromView(presentingView) 移出该父视图。

iOS 8
<UIViewControllerContextTransitioning>合计上加了viewForKey:方为有利于得
fromView 和 toView,但是以 Modal 转场里要是专注,从上面可以理解,Custom
模式下,presentingView 并无让 containerView
管理,这时通过viewForKey:措施来博 presentingView 得到的凡
nil,必须通过viewControllerForKey:博 presentingVC 后来抱。因此当
Modal 转场中,较稳当的道是由 fromVC 和 toVC 中获得 fromView 和
toView。

顺带一提,前面提到的UIView的接近方式transitionFromView:toView:duration:options:completion:会在
Custom 模式下工作,却和 FullScreen 模式有点不匹配。

等级同:非交互转场

这个等级如果开片桩事,提供转场代理并出于代理提供动画控制器。在转场代理协议里动画控制器和相互控制器都是可选实现的,没有落实或者返回
nil
的口舌虽然采取默认的转场效果。动画控制器是呈现转场效果的为主组成部分,代理部分非常简单,我们先动手定动画控制器吧。

Transition 解释

前言里从作为达到讲了转场,那在转场时有了呀?下图是于 WWDC 2013
Session 218 整理的,解释了转场时视图控制器和夫对应的视图在结构上的转移:

365体育网址 17

转场过程中,作为容器的父 VC 维护在多只子
VC,但当视图结构及,只保留一个子 VC 的视图,所以转场的庐山真面目是下同样光景(子
VC)的视图替换当前光景(子 VC)的视图以及相应的控制器(子
VC)的更迭,表现为当下视图消失和下一视图出现,基于此展开动画,动画的章程非常多,所以限制最终表现的功用就惟有你的想象力了。图备受之
Parent VC 可替换为 UIViewController, UITabbarController 或
UINavigationController 中的其他一样栽。

目前为止,官方支持以下几种植方法的自定义转场:

官方的支持包含了 iOS
中的大多数转场方式,还有雷同栽由定义容器被的转场并不曾到手系统的直白支持,不过凭借协议这种活的法门,我们仍然能够落实对从定义容器控制器转场的定制,在压轴环节我们拿促成这或多或少。

iOS 7 以商讨的艺术开了打定义转场的
API,协议的好处是不再拘泥于具体的某个类,只要是信守该谋的目标都能够参与转场,非常灵活。转场协议由5栽协议组成,在实质上中单需要我们提供其中的简单个或三个就可知实现绝大部分的转场动画:

1.转场代理(Transition Delegate):

打定义转场的率先步就是是提供转场代理,告诉系统利用我们提供的代办要无是系统的默认代理来施行转场。有如下三栽转场代理,对承诺者三种植档次的转场:

<UINavigationControllerDelegate> //UINavigationController 的 delegate 属性遵守该协议。
<UITabBarControllerDelegate> //UITabBarController 的 delegate 属性遵守该协议。
<UIViewControllerTransitioningDelegate> //UIViewController 的 transitioningDelegate 属性遵守该协议。

此间除了<UIViewControllerTransitioningDelegate>是 iOS 7
新增的商,其他两种植于 iOS 2 里虽在了,在 iOS 7
时扩充了立半种植协议来支撑于定义转场。

转场发生常,UIKit
将要求转场代理将提供转场动画的主导部件:动画控制器和相控制器(可选的);由我们兑现。

2.动画片控制器(Animation Controller):

最为关键之一些,负责上加视图以及实践动画;遵守<UIViewControllerAnimatedTransitioning>共谋;由我们兑现。

3.相互控制器(Interaction Controller):

透过相互手段,通常是手势来驱动动画控制器实现的卡通,使得用户会控制总体过程;遵守<UIViewControllerInteractiveTransitioning>商讨;系统已打包好现成的接近供应我们用。

4.转场环境(Transition Context):

提供转场中需要之数码;遵守<UIViewControllerContextTransitioning>谋;由
UIKit 在转场开始前别并提供给咱们付出的动画控制器和相互控制器采用。

5.转场协调器(Transition Coordinator):

但在转场动画发生的而并行执行其他的动画,其作用与其说协调不如说辅助,主要在
Modal
转场和互转场取消时以,其他时候挺少用到;遵守<UIViewControllerTransitionCoordinator>协商;由
UIKit 在转场时变,UIViewController 在 iOS 7
中新增了措施transitionCoordinator()返回一个遵循该协议的目标,且该法只有当该控制器处于转场过程被才返回一个此类对象,不参与转场时返回
nil。

总下,5单商量才待我们担心3独;实现一个最低限度可用之转场动画,我们特待提供方五独零件里的少个:转场代理及动画片控制器即可,还有一个转场环境是必不可少的,不过就是因为网提供;当越来越实现互动转场时,还待我们提供相互控制器,也来现成的类供应我们下。

UINavigationControllerDelegate

定制 UINavigationController
这种容器控制器的转场时,很吻合实现一个子类,自身集转场代理,动画控制器于一身,也方便使用,不过这样做有时候还要限定了它的行使范围,别人吧落实了和睦的子类时就是不克方便使用你的法力,这里运用的是拿转场代理封装成一个类似。

class SDENavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
    //在<UINavigationControllerDelegate>对象里,实现该方法提供动画控制器,返回 nil 则使用系统默认的效果。
    func navigationController(navigationController: UINavigationController, 
         animationControllerForOperation operation: UINavigationControllerOperation, 
                         fromViewController fromVC: UIViewController, 
                             toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        //使用上一节实现的 Slide 动画控制器,需要提供操作类型信息。
        let transitionType = SDETransitionType.NavigationTransition(operation)
        return SlideAnimationController(type: transitionType)
    }
}

要你于代码里呢您的控制器里如此设置代理:

//错误的做法,delegate 是弱引用,在离开这行代码所处的方法范围后,delegate 将重新变为 nil,然后什么都不会发生。
self.navigationController?.delegate = SDENavigationControllerDelegate()

足动用高引用的变量来引用新实例,且不可知运用当地变量,在控制器中新增一个变量来保障新实例就足以了。

self.navigationController?.delegate = strongReferenceDelegate

化解了已故引用的题材,这行代码应该置身哪里执行呢?很多总人口爱在viewDidLoad()举行一些配置工作,但于此处设置无法担保是可行之,因为这时控制器可能没有进入
NavigationController 的操纵器栈,self.navigationController回到的也许是
nil;如果是透过代码 push 其他控制器,在 push
前设置即可;prepareForSegue:sender:道是转场前移设置的终极一赖会,可以在这里安装;保险点,使用UINavigationController子类,自己作为代理,省去到处设置的分神。

可,通过代码设置终究显得挺烦且非安全,在 storyboard
里安同样劳永逸:在控件库里拖拽一个 NSObject 对象到相关的
UINavigationControler
上,在控制面板里用其项目设置为SDENavigationControllerDelegate,然后拖拽鼠标将那个安也代理。

末一步,像往一模一样触发转场:

self.navigationController?.pushViewController(toVC, animated: true)//or
self.navigationController?.popViewControllerAnimated(true)

于 storyboard 中经过安装 segue 时起启动画也将视同样的 Slide 动画。Demo
地址:NavigationControllerTransition。

Transition Coordinator

转场协调器(Transition
Coordinator)的上场时不多,但可是生死攸关先生。Modal
转场中,UIPresentationController类只能通过转场协调器来和动画控制器同步,并行执行其他动画片;这里她可以交互式转场结束时实行一个闭包:

func notifyWhenInteractionEndsUsingBlock(_ handler: (UIViewControllerTransitionCoordinatorContext) -> Void)

当转场由交互状态转变为未互状态(在手势交互过程被虽也手势结束时),无论转场的结果是做到或者为吊销,该方式都见面叫调用;得益于闭包,转场协调器可以以转场过程中的擅自阶段搜集动作并于互相中止后实施。闭包中之参数是一个死守<UIViewControllerTransitionCoordinatorContext>合计的目标,该目标由
UIKit
提供,和前的转场环境目标<UIViewControllerContextTransitioning>打算类似,它提供了相互转场的状态信息。

override func viewWillAppear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.doSomeSideEffectsAssumingViewDidAppearIsGoingToBeCalled()
    //只在处于交互转场过程中才可能取消效果。
    if let coordinator = self.transitionCoordinator() where coordinator.initiallyInteractive() == true{
        coordinator.notifyWhenInteractionEndsUsingBlock({
            interactionContext in
            if interactionContext.isCancelled(){
                self.undoSideEffects()
            }
        })
    }
}

而是相状态结束时毫无转场过程的巅峰(此后卡通控制器提供的转场动画根据交互结束时的状态继续或是返回到起状态),而是由于动画控制器来了却就一切:

optional func animationEnded(_ transitionCompleted: Bool)

只要实现了该方式,将以转场动画结束后调用。

UIViewController
可以经过transitionCoordinator()获得转场协调器,该法的文档中说除非以
Modal
转场过程遭到,该方法才返回一个同眼前转场相关的得力对象。实际上,NavigationController
的转场中 fromVC 和 toVC 也会回一个实惠对象,TabBarController
有点异样,fromVC 和 toVC 在转场中回到的凡 nil,但是作为容器的
TabBarController 可以采用该方法返回一个立竿见影对象。

转场协调器除了上面的少栽关键作用外,也于 iOS 8
中的适应性布局中任机要角色,可以查阅<UIContentContainer>谋中之道,其中响应尺寸及屏幕旋转事件之措施还蕴涵一个转场协调器对象,视图的这种变更为叫系统即广义上之
transition,参数中的转场协调器也鉴于 UIKit
提供。这个话题有点高于本文的克,就未深入了,有需要的话可以查看文档和有关
session。

   

从定义容器控制器转场

压轴环节我们用贯彻如此一个作用:

365体育网址 18
365体育网址 19

Demo 地址:CustomContainerVCTransition。

解析一下思路,这个控制器和 UITabBarController 在作为上比相似,只是
TabBar 由下跑至了点。我们可采取 UITabBarController
子类,然后打一个伪 TabBar 放在顶部,原来的 TabBar
则躲,行为及了一样,使用 UITabBarController
子类的补是可以减轻实现转场的承担,不过,有时候这样的子类不是公想只要之,UIViewController
子类能够提供再多之自由度,好吧,一个毕模拟 UITabBarController 行为之
UIViewController
子类,实际上自己没有想到非得这么做的由,但本身眷恋得有亟待定制好的器皿控制器的面貌,这多亏本节要追究的。Objc.io
也讨论了这话题,文章的末梢把实现相互之间控制作为作业留了下。珠玉在前,我便立在大牛的肩上继续是话题吧。Objc.io
的即时首稿子写得比早采用了 Objective-C
语言,如果只要读者先失读就首稿子更持续读本节的内容,难免割裂,所以本节要开始讨论这话题吧,最终效果使上面所示,在由定义之容器控制器中落实互动控制切换子视图,也得通过填写了
UIButton 的 ButtonTabBar 来兑现 TabBar 一样行为之 Tab
切换,在通过手势切换页面时 ButtonTabBar 会实现渐变色动画。ButtonTabBar
有格外特别扩展性,改造或替换为另外视图还是产生许多以场景的。

进阶

是不是当本文中实现之例子的动画片效果最好过粗略?的确非常简单,与 VCTransitionsLibrary 这样的转场动画库提供的十种动画效果比是挺简单的,不过即便动画而言,与本文示例的真相是平的,它们还是针对性
fromView 和 toView
的完好进行的动画片,但以职能上更错综复杂。我当本文中屡强调转场动画的本质是凡指向将要消失的目前视图和即将面世的下一屏幕的始末展开动画,「在动画控制器里,参与转场的视图只有
fromView 和 toView
之分,与转场方式无关。转场动画的末梢效果才限制于公的想象力」,当然,还有你的实现能力。

本文前面的目的是援你熟悉转场的百分之百经过,你呢看看了,转场动画里转场部分的兑现其实深简短,大部分苛的转场动画与本文范例里大概的转场动画相比,复杂的部分在动画部分,转场的有的还是一模一样的。因此,学习了前的情后并无可知帮助而马上就能落实
Github
上那些热门的转场动画,它们变成热点之原故在于动画本身,与转场本身关系不大,但其与转场结合后虽生出矣神奇的力量。那学习了当进阶的本章能立时兑现那些热门之转场效果也?有或,有些功能其实深简短,一点虽显,还有部分职能涉及的艺属于本文主题之外的情,我会见为闹有关的提醒就不透了。

本章的进阶分为两个组成部分:

  1. 案例分析:动画的主意很多,有些并无普遍,有些只是简单到让人惊愕的咬合,只是你无了解了用无知情哪些兑现,一旦了解了就算不再是难事。尽管这些动画片本身并无属转场技术这主题,但和转场动画组成后往往具有惊艳的视觉效果,这部分以提供有落实此类转场动画的笔触,技巧和工具来扩充视野。有很多动画类型我啊并未品味过,可能的话语我会继续创新一些妙趣横生的案例。
  2. 从今定义容器转场:官方支持四种植办法的转场,而且这些为可应付绝大多数急需了,但仍然略地方无法照顾。本文一直通过探索转场的疆界的道来总结运用办法与陷阱,在本文的压轴部分,我们将挣脱系统的格来促成由定义容器控制器的转场效果。

前言

屏幕左边缘右滑返回,TabBar
滑动切换,你是否爱并生凭就半个操作,甚至以为 App
不支持即时类操作的语句简直反人类?这点儿个操作以大屏时代大提升了操作效率,其背后的技巧就是今日的主题:视图控制器转换(View
Controller Transition)。

探望图控制器中的视图显示在屏幕上出少种办法:最根本的点子是内嵌在容器控制器中,比如
UINavigationController,UITabBarController,
UISplitController;由另外一个视图控制器显示她,这种措施一般给称呼模态(Modal)显示。View
Controller Transition 是什么?在 NavigationController 里 push 或 pop
一个 View Controller,在 TabBarController 中切换至另外 View
Controller,以 Modal 方式展示另外一个 View Controller,这些都是 View
Controller Transition。在 storyboard 里,每个 View Controller 是一个
Scene,View Controller Transition 便是自一个 Scene 转换到另外一个
Scene;为好,以下对 View Controller Transition 的中文称呼采用
Objccn.io 中的翻「转场」。

以 iOS 7
之前,我们不得不用系统提供的转场效果,大部分上够用,但无非是够而已,总归会时有发生各种非如意的略地方,但咱可无力改变;iOS
7 开放了有关 API
允许我们本着转场效果进行宏观定制,这绝巧了,自定义转场动画与针对互相手段的支持带动了极其可能。

正文并非豪华的转场动画教程,相反,文中的转场动画效果还怪简便,但本文的情节连无略,我将拉动您追转场背后的体制,缺陷以及落实过程被的技巧与陷阱。阅读本文需要读者至少要针对
ViewController 和 View
的构造与协和来基本的打听,最好和谐亲手促成了一两栽转场动画。如果你针对这个感到并未信心,推荐观看官方文档:View
Controller Programming Guide for
iOS,学习之文档将会见于你再爱懂本文的始末。对你想深造的小节,我欲您协调亲手写下这些代码,一步步地看正在作用是何许兑现之,至少对本身而言,看各种有关材料时不过生字面意义及之知晓,正是一步步之考查才会让自身了解每一个步骤。本文涉及的情节比较多,为了避免篇幅过长,我不过吃闹重点代码而无是由新建工程初步让君各个一个手续。本文基于
Xcode 7 以及 Swift 2,Demo
合集地址:iOS-ViewController-Transition-Demo。

动画控制及 CAMediaTiming 协议

以此协议定义了一致法时间体系,是决定动画进度的要紧。UIView Animation
是运 Core Animation 框架实现的,也便是用 UIView 的 CALayer
对象实现之卡通,而 CALayer 对象遵守该协议。

以交互控制器的小节里自己自从了一个只要,交互控制器就如一个视频播放器一样控制着转场动画这个视频的快。依靠
CAMediaTiming 这套商,我们好以 CALayer
对象及对丰富的卡通片实现控制。官方的实现充分有或吧是用了千篇一律的伎俩。CAMediaTiming
协议被出以下几个属性:

//speed 作用类似于播放器上控制加速/减速播放,默认为1,以正常速度播放动画,为0时,动画将暂停。
var speed: Float 
//修改 timeOffset 类似于拖动进度条,对一个2秒的动画,该属性为1的话,动画将跳到中间的部分。
//但当动画从中间播放到预定的末尾时,会续上0秒到1秒的动画部分。
var timeOffset: CFTimeInterval
//动画相对于父 layer 延迟开始的时间,这是一个实际作用比字面意义复杂的属性。 
var beginTime: CFTimeInterval  

Core Animation 的文档中提供了哪暂停和回复动画的言传身教:How to pause the
animation of a layer
tree。我们用之以实现对速度的支配,这种办法对中间的子视图上长的动画片也能够落实控制,这正是我们用的。假设以
containerView 中之 toView 上实施一个简单易行的沿 X 轴方向移动 100
单位之号移动画,由executeAnimation()方式执行。下面是采取手势控制该动画进度的着力代码:

func handlePan(gesture: UIPanGestureRecognizer){
    switch gesture.state{
    case .Began:
        //开始动画前将 speed 设为0,然后执行动画,动画将停留在开始的时候。
        containerView.layer.speed = 0
        //在transitionContext里,这里替换为 animator.animateTransition(transitionContext)。
        executeAnimation() 
    case .Changed:
        let percent = ...
        //此时 speed 依然为0,调整 timeOffset 可以直接调整动画的整体进度,这里的进度控制以时间计算,而不是比例。
        containerView.layer.timeOffset = percent * duration
    case .Ended, .Cancelled:
        if progress > 0.5{
            //恢复动画的运行不能简单地仅仅将 speed 恢复为1,这是一套比较复杂的机制。
            let pausedTime = view.layer.timeOffset
            containerView.layer.speed = 1.0 
            containerView.layer.timeOffset = 0.0
            containerView.layer.beginTime = 0.0
            let timeSincePause = view.layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
            containerView.layer.beginTime = timeSincePause
        }else{/*逆转动画*/}
        default:break
    }
}

磋商上了

模仿 UITabBarControllerDelegate 协议的 ContainerViewControllerDelegate
协议:

//在 Swift 协议中声明可选方法必须在协议声明前添加 @objc 修饰符。
@objc protocol ContainerViewControllerDelegate{
    func containerController(containerController: SDEContainerViewController, animationControllerForTransitionFromViewController 
                                          fromVC: UIViewController, 
                           toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
    optional func containerController(containerController: SDEContainerViewController, interactionControllerForAnimation 
                                      animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
}

当容器控制器SDEContainerViewController恍如中,添加转场代理属性:

weak var containerTransitionDelegate: ContainerViewControllerDelegate?

代办的定点就是是供动画控制器和交互控制器,系统打包的UIPercentDrivenInteractiveTransition类才是调用了转场环境目标的相应措施而已,执行navigationController.pushViewController(toVC, animated: true)及时类似语句触发转场后
UIKit
就接管了剩下的作业,再汇总文档的叙说,可明白转场环境就是是促成即通的中坚。

当篇章前面的一对里转场环境目标的意只是供关乎转场过程的音和状态,现在内需我们兑现该谋,并且实现藏的那片任务。
<UIViewControllerContextTransitioning>磋商里之多边道还是得贯彻的,不过本咱们先实现非交互转场的一对,实现这个是十分简短的,主要是调用动画控制器执行转场动画。在「实现分析」一节约里我们视实现转场的代码只发十几实施而就,动画控制器需要举行的只是处理视图和动画片的片,转场环境目标则只要负责管理子
VC,通过SDEContainerViewController提供 containerView 以及 fromVC 和
toVC,实现并无是难事。显然由我们贯彻之自定义容器 VC
来供转场环境目标是无限确切的,并且转场环境目标应当是私房的,其初始化方法极其启动转场的艺术如下:

class ContainerTransitionContext: NSObject, UIViewControllerContextTransitioning{
    init(containerViewController: SDEContainerViewController, 
                   containerView: UIView, 
       fromViewController fromVC: UIViewController, 
           toViewController toVC: UIViewController){...}

    //非协议方法,是启动非交互式转场的便捷方法。
    func startNonInteractiveTransitionWith(delegate: ContainerViewControllerDelegate){
        //转场开始前添加 toVC,转场动画结束后会调用 completeTransition: 方法,在该方法里完成后续的操作。
        self.privateContainerViewController.addChildViewController(privateToViewController)
        //通过 ContainerViewControllerDelegate 协议定义的方法生成动画控制器,方法名太长了略去。
        self.privateAnimationController = delegate.XXXmethod
        //启动转场并执行动画。
        self.privateAnimationController.animateTransition(self)
    }
    //协议方法,动画控制器在动画结束后调用该方法,完成管理子 VC 的后续操作,并且考虑交互式转场可能取消的情况撤销添加的子 VC。
    func completeTransition(didComplete: Bool) {
        if didComplete{
            //转场完成,完成添加 toVC 的工作,并且移除 fromVC 和 fromView。
            self.privateToViewController.didMoveToParentViewController(privateContainerViewController)
            self.privateFromViewController.willMoveToParentViewController(nil)
            self.privateFromViewController.view.removeFromSuperview()
            self.privateFromViewController.removeFromParentViewController()
        }else{
            //转场取消,移除 toVC 和 toView。
            self.privateToViewController.didMoveToParentViewController(privateContainerViewController)
            self.privateToViewController.willMoveToParentViewController(nil)
            self.privateToViewController.view.removeFromSuperview()
            self.privateToViewController.removeFromParentViewController()
        }
        //非协议方法,处理收尾工作:如果动画控制器实现了 animationEnded: 方法则执行;如果转场取消了则恢复数据。
        self.transitionEnd()
    }
}

SDEContainerViewController类似吃,添加转场环境性:

private var containerTransitionContext: ContainerTransitionContext?

并修改transitionViewControllerFromIndex:toIndex道实现由定义容器 VC
转场动画:

private func transitionViewControllerFromIndex(fromIndex: Int, toIndex: Int){
    if self.containerTransitionDelegate != nil{
        let fromVC = viewControllers![fromIndex]
        let toVC = viewControllers![toIndex]
        self.containerTransitionContext = ...//利用 fromVC 和 toVC 初始化。
        self.containerTransitionContext?.startNonInteractiveTransitionWith(containerTransitionDelegate!)
    }else{/*没有提供转场代理的话,则使用最初没有动画的转场代码,或者提供默认的转场动画*/}
}

诸如此类咱们不怕使协议落实了由定义容器控制器的转场动画,可以下第三着的动画控制器来实现不同之功用。

不过若是留意就几只对象之间错综复杂的援关系避免引用循环,关系图如下:

365体育网址 20

相关文章