iOS默認的
presentViewController
的切換動畫是從底部推入,消失是從頂部推出。但是,因為iOS系統默認的是適配所有轉場上下文的。而針對特定的轉場上下文,我們能做出更好的效果。
Tips:所謂的轉場上下文,就是轉場的開始View和結束View,以及對應的ViewController
最終的效果
首先寫出一個CollectionView
,每個Cell是一個圖片,由於本文的核心是如何轉場,所以CollectionView
的部分略過。寫完了之後,是這樣的效果
點擊某一個CollectionView Cell查看大圖,再點擊大圖圖片消失
iOS 8之後,我們可以通過設置
ViewController
的transitioningDelegate
來設置代理來處理轉場動畫。
通過文檔,可以看到transitioningDelegate
是一個實現UIViewControllerTransitioningDelegate
協議的對象,先看看這個協議,本文主要利用以下兩個方法
著兩個方法的目的是,提供一個遵循UIViewControllerAnimatedTransitioning
協議的對象,然後又這個對象來實際處理專場。通過名字就可以看出來,一個是處理present一個是處理dismiss。
本文的設計是讓ViewController
來處理轉場,在didSelectItemAtIndexPath
中,設置pvc的轉場代理
dvc.transitioningDelegate = self
然後,寫一個extension來實現協議
extension ViewController:UIViewControllerTransitioningDelegate{
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return nil
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return nil
}
}
這時候,再運行項目,會發現沒有任何變化,還是默認的轉場方式。因為我們還沒有提供實際的動畫 。當上述兩個代理方法返回nil的時候,系統會使用默認的方式
新建一個文件,命名為Animator.swift,然後新建一個類,處理Present的轉場(Dismiss類似),實現UIViewControllerAnimatedTransitioning協議
class PresentAnimator: NSObject,UIViewControllerAnimatedTransitioning{
}
這時候,會報錯沒有實現協議,然後,我們添加協議方法,這時候的Animator.swift如下
import Foundation
import UIKit
class PresentAnimator: NSObject,UIViewControllerAnimatedTransitioning{
let duration = 0.5 //動畫的時間
var originFrame = CGRectZero //點擊Cell的frame
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return duration
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
}
}
簡單介紹下這裡的協議方法
transitionDuration,返回轉場動畫的時間 animateTransition,進行實際的轉場動畫,通過參數transitionContext(轉場上下文)來獲取轉場的fromView,toView,fromViewController,toViewController。所以,開發者要做的就是
把toView添加到轉場ContainView中,並且定義好toView的初始位置和狀態 定義好FromView和ToView的轉場結束時候的狀態 創建動畫基於上述的原理,我們修改實際的動畫
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containView = transitionContext.containerView()
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let finalFrame = toView.frame
let xScale = originFrame.size.width/toView.frame.size.width
let yScale = originFrame.size.height/toView.frame.size.height
toView.transform = CGAffineTransformMakeScale(xScale, yScale)
toView.center = CGPointMake(CGRectGetMidX(originFrame), CGRectGetMidY(originFrame))
containView?.addSubview(toView)
UIView.animateWithDuration(duration, animations: { () -> Void in
toView.center = CGPointMake(CGRectGetMidX(finalFrame), CGRectGetMidY(finalFrame))
toView.transform = CGAffineTransformIdentity
}) { (finished) -> Void in
transitionContext.completeTransition(true)
}
}
然後,在ViewController.swfit中,添加一屬性
let presentAnimator = PresentAnimator()
修改didSelectItemAtIndexPath
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let dvc = DetailViewController()
dvc.image = UIImage(named: "image.jpg")
dvc.transitioningDelegate = self
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FullImageCell
presentAnimator.originFrame = cell.convertRect(cell.imageview.frame, toView: nil)
self.presentViewController(dvc, animated: true, completion: nil)
}
修改代理方法
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return presentAnimator
}
這時候的動畫效果如Gif
在Animator.swift中添加一個新的類
class DismisssAnimator:NSObject,UIViewControllerAnimatedTransitioning{
let duration = 0.6
var originFrame = CGRectZero
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return duration
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containView = transitionContext.containerView()
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! //Collection View
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! //全屏的imageview
let xScale = originFrame.size.width/toView.frame.size.width
let yScale = originFrame.size.height/toView.frame.size.height
containView?.addSubview(toView)
containView?.bringSubviewToFront(fromView)
UIView.animateWithDuration(duration, animations: { () -> Void in
fromView.center = CGPointMake(CGRectGetMidX(self.originFrame), CGRectGetMidY(self.originFrame))
fromView.transform = CGAffineTransformMakeScale(xScale, yScale)
}) { (finished) -> Void in
transitionContext.completeTransition(true)
}
}
}
然後,在ViewController.swift中didSelectItemAtIndexPath
設置presentAnimator.frame下一行添加
dismissAnimator.originFrame = cell.convertRect(cell.imageview.frame, toView: nil)
其中,dismissAnimator是ViewController的一個屬性
let dismissAnimator = DismisssAnimator()
然後,在代理方法中,返回
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return dismissAnimator;
}
最終的效果