本文是學(xué)習(xí)了這篇博客的一點個人心得,給大家分享一下。
今天實現(xiàn)了一個簡單的自定義轉(zhuǎn)場動畫
邏輯實現(xiàn)
- 在A控制器創(chuàng)建一個遵守
UIViewControllerTransitioningDelegate
協(xié)議的代理對象 - 設(shè)置B的
transitioningDelegate
為步驟1中創(chuàng)建的代理對象 - 在代理對象中實現(xiàn)相對應(yīng)的方法,再方法中返回相對應(yīng)的動畫管理器
- 調(diào)用
presentViewController:animated:completion:
并把參數(shù)animated
設(shè)置為true
- 當(dāng)轉(zhuǎn)場的時候系統(tǒng)會從代理對象中實現(xiàn)的方法中尋找相對應(yīng)得動畫控制器,具體動畫過程會在動畫控制器中實現(xiàn)
具體步驟
- 創(chuàng)建2個控制器,分別為A控制器和B控制器。
- 創(chuàng)建B的
transitioningDelegate
,因為內(nèi)容比較少,所以設(shè)置為A控制器本身B.transitioningDelegate = self
- 在A和B中實現(xiàn)控制器的切換,也就是
public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)
public func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?)
- 使A遵守
TransitioningDelegate
并實現(xiàn)協(xié)議中的以下方法
// return:Present時的動畫管理器
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animator = TransitionAnimator()
// animator就是相對應(yīng)的管理器,管理器的具體實現(xiàn)請往下看
animator.transition = .Present
return animator
}
// return:Dismiss時的動畫管理器
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animator = TransitionAnimator()
animator.transition = .Dismiss
return animator
}
-
TransitionAnimator
是遵守UIViewControllerAnimatedTransitioning
的動畫管理器,代理方法如下
// 轉(zhuǎn)場動畫持續(xù)的時間
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval{
return 2.0
}
// 轉(zhuǎn)場動畫具體的實現(xiàn)
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// present dismiss
// fromViewController A B
// toViewController B A
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
// 轉(zhuǎn)成動畫中的容器View
let containerView = transitionContext.containerView()
let fromView = fromViewController?.view
let toView = toViewController?.view
// transition 是自己寫的屬性,用來分別是present和Dismiss
if self.transition == .Present{
presentAnimation(transitionContext, fromView: fromView!, toView: toView!, containerView: containerView!,fromController: fromViewController!, toController: toViewController!)
}else{
dismissAnimation(transitionContext, fromView: fromView!, toView: toView!, containerView: containerView!, fromController: fromViewController!, toController: toViewController!)
}
}
- 再去實現(xiàn)present和dismiss相對應(yīng)的動畫即可
// present 動畫的實現(xiàn)
func presentAnimation(transitionContext:UIViewControllerContextTransitioning,fromView:UIView,toView:UIView,containerView:UIView,fromController:UIViewController,toController:UIViewController){
// 設(shè)置B控制器的錨點為右上角
toView.layer.anchorPoint = CGPoint(x: 1, y: 0)
// 可以獲得B控制器最終的frame
toView.frame = transitionContext.finalFrameForViewController(toController)
// 將B控制器的View 添加的containerView上
containerView.addSubview(toView)
// 使B控制器逆時針旋轉(zhuǎn)90度
toView.layer.transform = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0, 0, 1)
toView.alpha = 0
let transitionDuration = self.transitionDuration(transitionContext)
// 彈性動畫
UIView.animateWithDuration(transitionDuration, // 持續(xù)時間
delay: 0, // 延遲多久執(zhí)行
usingSpringWithDamping: 0.6, // 彈性強度
initialSpringVelocity: 0, // 初始速度
options: .CurveLinear, // 彈性方案
animations: { () -> Void in
toView.alpha = 1
toView.layer.transform = CATransform3DIdentity
}) { (finished: Bool) -> Void in
//再完成之后,記得去實現(xiàn)轉(zhuǎn)場動畫結(jié)束的方法
let wasCancelled = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!wasCancelled)
}
}
func dismissAnimation(transitionContext:UIViewControllerContextTransitioning,fromView:UIView,toView:UIView,containerView:UIView,fromController:UIViewController,toController:UIViewController){
let transitionDuration = self.transitionDuration(transitionContext)
// 將A控制器的View添加到B控制器之下
containerView.insertSubview(toView, belowSubview: fromView)
UIView.animateWithDuration(transitionDuration * 0.5, animations: { () -> Void in
fromView.layer.transform = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0, 0, 1)
fromView.alpha = 0
}) { (finished: Bool) -> Void in
let wasCancelled = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!wasCancelled)
}
}
這樣轉(zhuǎn)場動畫就實現(xiàn)了
注意點: 控制器的modalPresentationStyle
也就是模態(tài)呈現(xiàn)風(fēng)格模式是Full Screen
,你也可以改成custom
來自定義添加視圖,不過當(dāng)style
為custom
的時候fromView
不會被移除,而且在轉(zhuǎn)成動畫開始的時候要在動畫管理中實現(xiàn)以下方法
_toVC.beginAppearanceTransition(true,animated:true)
和
func animationEnded(transitionCompleted: Bool) {
toVC.endAppearanceTransition()
}
支持手勢的轉(zhuǎn)場動畫
- 其實在present到B的時候會先執(zhí)行代理的
interactionControllerForPresentation
這個方法來獲得交互控制器,如果返回值為nil的話,才會執(zhí)行剛剛寫的動畫,現(xiàn)在我們要使用到這個方法了 - 首先新建一個類InteractionController,繼承于
UIPercentDrivenInteractiveTransition
,在A中新建一個手勢var gesture:UIScreenEdgePanGestureRecognizer
,設(shè)置手勢的action
為present
到B,然后實現(xiàn)代理方法,當(dāng)用手勢的時候返回剛剛新建的類,并把手勢傳給他
func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?{
// isPanGestureRecognizer是用來判斷我是用手勢還是用的按鈕執(zhí)行的present的動畫
return self.isPanGestureRecognizer ? InteractionController.init(gesture: self.gesture) : nil
}
- 在新建的類中新添加手勢的方法
/// 當(dāng)手勢有滑動時觸發(fā)這個函數(shù)
func gestureAction(gestureRecognizer: UIScreenEdgePanGestureRecognizer) {
switch gestureRecognizer.state {
case .Began: break
// 在此更新轉(zhuǎn)成動畫的進(jìn)度,percentForGesture方法是自己寫的用來計算進(jìn)度的值,可以自行設(shè)置,具體內(nèi)容請看demo
case .Changed: self.updateInteractiveTransition(self.percentForGesture(gesture)) //更新進(jìn)度
case .Ended:
// 手勢滑動超過5分之一的時候完成轉(zhuǎn)場,否則取消轉(zhuǎn)場動畫
if self.percentForGesture(gestureRecognizer) >= 0.2 {
self.finishInteractiveTransition()
}else {
self.cancelInteractiveTransition()
}
default: self.cancelInteractiveTransition()
}
}
- 這樣的話手勢滑動的功能就能實現(xiàn)了
自定義push和pop動畫
- 基本原理和剛剛的方法是一樣的,不過需要設(shè)置
navigationController
的delegate
,同時在代理中實現(xiàn)代理方法,也是返回動畫管理器,在此就沒有多寫動畫管理器,也是用的剛剛的動畫器了。
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
if operation == .Push{
let animator = TransitionAnimator()
animator.transition = .Present
return animator
}else{
return nil
}
}
End
Github上的ColinEberhardt的VCTransitionsLibrary已經(jīng)封裝了一系列的VC自定義切換動畫效果,可以在此基礎(chǔ)上修改獲得更炫酷的轉(zhuǎn)場動畫,Come On!
如果有時間的話可以自己試著做一下,同時希望各位的多多指導(dǎo),歡迎互相交流