首先, 我們得了解什麼是轉場動畫.
1. 轉場動畫即是對一個view呈現和關閉時所做的動畫, 叫轉場動畫.
動畫是如何做出來的呢?
一. 了解CALayer與CGContext和UIView之間的關係
1. 以前我們學習UIView的時候, 應該知道, 當創建一個UIView的時候,系統會默認將UIView的LayerClass設置為layer類型.那麼什麼是layer呢?
-1.1 layer即是圖層, 是CALayer類型的實例. 而CALayer包含在QuartzCore框架中,這是一個跨平台的框架,即可以用在ios中又可以用在Mac OS X中.在使用Core Animation開發動畫的本質就是將CALayer中的內容轉化為位圖從而供硬件操作,所以要熟練掌握動畫操作必須先熟悉CALayer.
-1.2 又多一個問題了, 那麼CALayer是什麼?首先, UIView有一個屬性是layer, 在這個屬性有下面這麼一段注析
: public var layer: CALayer { get } // returns view's layer. Will always return a non-nil value. view is layer's delegate
很明顯, 這個屬性返回一個layer, 並且當前View是這個Layer的代理. 那麼既然是代理, 說明當前view可以實現Layer的代理方法來對當前view的layer進行操作.
那麽就探一下CALayer內部都有些啥東西:
public init()
public init(layer: AnyObject)
public func presentationLayer() -> AnyObject?
public func modelLayer() -> AnyObject
public class func defaultValueForKey(key: String) -> AnyObject?
public class func needsDisplayForKey(key: String) -> Bool
public func shouldArchiveValueForKey(key: String) -> Bool
public var bounds: CGRect
public var position: CGPoint
等等......太多了就不列出來了, 這裡只做個簡單的分析.
很明顯CALayer內部的方法都是用來操作layer的一些構造方法,屬性等. 至上而下可以很清晰的明白,CALayer不是UIKit的東西, 是在QuartzCore框架下聲明的一個類.
-1.3 說這麼多, 那麼layer到底有什麼作用?
首先我們通過CALayer的一個代理方法來說明下:
override func drawLayer(layer: CALayer, inContext ctx: CGContext)
通過這個代理方法, 可以對圖層進行繪製. 我kao, 這裡有多了一個CGContext的類, 有是神馬東西? 讓我們來逐步分析:
CGContext ctx : 圖形上下文
CALayer layer: 當前layer
通過上面的代理方法, 可以拿到圖形上下文, 而圖形上下文裡面存放著當前layer繪製圖形的所有數據, 既然是專門用來繪製圖形的上下文, 那麼我們只要通過對上下文內的數據進行需修改, 就可以完成在layer上面的繪製. 說到這裡應該很清晰了吧?讓我們直接點進去CGContext裡面看看有什麼方法可以用來修改上下文的數據:
public func CGContextScaleCTM(c: CGContext?, _ sx: CGFloat, _ sy: CGFloat)
@available(iOS 2.0, *)
public func CGContextTranslateCTM(c: CGContext?, _ tx: CGFloat, _ ty: CGFloat)
等等...這裡不一一列出來了, 因為太多了, 這裡只做簡單分析. 很明顯, 這兩個方法是通過傳入一個 c : 上下文 , 再通過傳入參數對上下文內的數據進行修改來繪製Scale和Translate.
好了,讓我們總結一下layer的作用吧. 通過上面一些雜亂東西的分析,layer其實就是一個畫板, 通過CALayer內部的方法可以修改畫板的一些基本屬性, 通過CALayer的代理方法可以拿到畫板繪製圖形的各種數據, 通過對圖形上下文數據的修改來進行圖片繪製,並且最終畫在Layer上.說白了, Layer就是一個用來呈現圖形的畫板,沒有Layer, 就看不見任何東西.
二. 轉場動畫的實現
上面第一點也說了, 轉場動畫是View呈現和關閉的時候的過渡動畫.
很簡單, 既然是View的動畫,我們可以通過UIView提供的動畫方法來實現:
@available(iOS 4.0, *)
public class func animateWithDuration(duration: NSTimeInterval, delay: NSTimeInterval, options: UIViewAnimationOptions, animations: () -> Void, completion: ((Bool) -> Void)?)
@available(iOS 4.0, *)
public class func animateWithDuration(duration: NSTimeInterval, animations: () -> Void, completion: ((Bool) -> Void)?)
@available(iOS 4.0, *)
public class func animateWithDuration(duration: NSTimeInterval, animations: () -> Void)
@available(iOS 4.0, *)
public class func transitionWithView(view: UIView, duration: NSTimeInterval, options: UIViewAnimationOptions, animations: (() -> Void)?, completion: ((Bool) -> Void)?)
等等, 這些都是UIView內部提供的實現動畫的方法.但是這些方法並不能滿足我們的全部需求,因為在modal一個控制器的時候, 我們可以通過這些方法控制View在即將顯示和正在顯示的這段時間內做動畫, 而不能對控制器,View和ContainerView進行操作.對控制器的呈現和關閉做一個自定義的動畫, 即是轉場動畫.
我靠, 這裡又多了一個ContainerView, 又是神馬東西?
ContainerView: 容器視圖, 用於存放動畫所需要的控件,所有跟動畫有關的控件都放在裡面.
好, 讓我們舉個例子來簡單的說明Swift中轉場動畫的使用.我盡量說得詳細點, 耐心看下去就懂了.
需求: 在一個NavigationBar控制器上點擊RightButton, 呈現一個自定義尺寸,動畫的控制器.
分析: 首先控制器的層級結構是 UIWindow -> RootViewController -> UINavigationController
此時我們需要了解幾個UIViewController中的屬性:
1.presentedViewController: 這是一個被當前控制器或者其最近的父控制器所modal的控制器
2.presentingViewController: presentedViewController的根控制器, 即最遠的祖先控制器
3.transitioningDelegate : UIViewControllerTransitioningDelegate : 轉場動畫代理
好, 既然UIViewController提供了一個轉場動畫的代理, 那麼我們只需要實現下代理方法, 即可以完成轉場動畫了. 好像很快要實現動畫了, 很緊張的趕腳.
第一步:
首先創建一個類, 並且讓它成為transitioningDelegate的代理
第二步:
實現代理方法:
1作用: 當自定義顯示控制器(UIPresentationController)呈現一個控制器時管理視圖層次結構, 向代理發送請求. (這個自定義顯示控制器(UIPresentationCtroller)是用來管理modal的顯示佈局)
參數: 這個方法需要我們返回一個UIPresentationController的實例
運用: 接下來,我們需要手動創建一個UIPresentationController實例,並且實現其內部構造方法來對modal控制器的佈局, 最後返回該對象即可.
1. func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2作用:返回一個遵守UIViewControllerAnimatedTransitioning協議的對象, 設置在控制器
呈現的時候通過該對象實現UIViewControllerAnimatedTransitioning中的代理方法.
協議: UIViewControllerAnimatedTransitioning
協議方法: 1.
public
func
transitionDuration(transitionContext:
UIViewControllerContextTransitioning
?) ->
NSTimeInterval (功能: 返回動畫時長)
2.
public
func
animateTransition(transitionContext:
UIViewControllerContextTransitioning
) (功能: 控制器的呈現和關閉都會掉用此方法)
3.
optional
public
func
animationEnded(transitionCompleted:
Bool
) (功能: 動畫結束時調用此方法)
分析: 該協議的方法傳入了一個新的東西: transitionContext , 轉場上下文, 裡面存放著有關轉場動畫的所有數據.
運用: 1. 通過
transitionContext.
viewForKey
(
UITransitionContextToViewKey
)! 可以拿到需要呈現的控制器的view
2. 通過
transitionContext.
viewForKey
(
UITransitionContextFromViewKey
)! 可以拿到需要發起控制器的view
3. 通過拿到的控制器的view, 在
public
func
animateTransition(transitionContext:
UIViewControllerContextTransitioning
) 方法中判斷控制器的呈現和關閉來實現動畫.
2. func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
作用:返回一個遵守UIViewControllerAnimatedTransitioning協議的對象, 設置在控制器關閉的時候通過該對象實現UIViewControllerAnimatedTransitioning中的代理方法.
3.func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
好了,基本上通過以上的操作, 就可以完成自定義的轉場動畫了. 真的感覺不費吹灰之力.