比較有難度的swift自定義轉場動畫

SamShackelf 8年前發布 | 7K 次閱讀 Apple Swift開發 Swift

一 轉場效果圖和采用轉場方式

1 轉場效果圖 :

這里寫圖片描述

2 采用方式 (方法):

—-> 2.1 自定義轉場動畫

—-> 2.2 協議

二 轉場實現需要獲取的東西

1 獲取轉場前圖片的frame

2 設置一張臨時imageView作為轉場圖片(圖片并不是真實存在的)

3 獲取圖片放大展示的frame

三 轉場圖解

這里寫圖片描述

四 轉場動畫思想

1 通過在實現轉場動畫的類中定義協議方法,定義代理屬性,明確誰可以提供需要的frame和imageView,將對方設置為代理,讓代理提供需求,達到轉場目的.

2 明確代碼的書寫位置,在哪里可以實現轉場動畫.

五 協議(彈出動畫)

1 協議書寫位置

這里寫圖片描述

2 協議方法 : (可以讓外界提供需要的東西)

 //MARK: - 定義協議用來拿到圖片起始位置;最終位置和圖片
protocol PersentDelegate : class {
    //起始位置
    func homeRect(indexPath : NSIndexPath) ->CGRect
    //展示圖片的位置
    func photoBrowserRect(indexPath : NSIndexPath) ->CGRect
    //獲取imageView(用這張臨時的圖片來實現動畫效果)
    func imageView(indexPath : NSIndexPath) ->UIImageView
}

3 在該該類中設置代理

//設置代理(代理一般都是用weak修飾)
    weak var presentDelegate : PersentDelegate?

4 實現轉場動畫的代碼書寫位置 : (上一篇送已經說明轉場動畫的書寫位置)

—-> 4.1 得到一張圖片

—-> 4.2 得到home中圖片的起始frame

—-> 4.3 展示圖片的frame

—-> 4.4 完成動畫

—-> 4.5 圖片由透明到清晰,通過alpha來實現

//獲取轉場的上下文:可以通過上下文獲取到執行動畫的view
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        isPresented ? animateForPresentView(transitionContext) : animateForDismissed(transitionContext)
    }
    //彈出
    func animateForPresentView(transitionContext: UIViewControllerContextTransitioning) {
        //1 取出彈出的view
        let presentView = transitionContext.viewForKey(UITransitionContextToViewKey)!
        //2 將彈出的view加入到contentView中
        transitionContext.containerView()?.addSubview(presentView)
        //3 執行動畫
        //3.1 獲取需要執行的imageView
        guard let persentDelegate = presentDelegate else {
            return
        }
        guard let indexPath = indexPath else {
            return
        }
        //調用方法,得到一張圖片
        let imageView = persentDelegate.imageView(indexPath)
        //將圖片添加到父控件中
        transitionContext.containerView()?.addSubview(imageView)
        //設置imageView的起始位置
        imageView.frame = persentDelegate.homeRect(indexPath)
        presentView.alpha = 0
        //設置彈出動畫的時候背景顏色為黑色
        transitionContext.containerView()?.backgroundColor = UIColor.blackColor()
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
            //設置展示圖片的位置
            imageView.frame = persentDelegate.photoBrowserRect(indexPath)
            }) { (_) -> Void in
            //修改圖片透明度
            presentView.alpha = 1.0
            //移除圖片
            imageView.removeFromSuperview()
            transitionContext.containerView()?.backgroundColor = UIColor.clearColor()
            //完成動畫
            transitionContext.completeTransition(true)
        }
    }

六 傳值

1 如何通過協議獲取home中某張圖片的frame值?

—-> 1.1 通過在扎UN擦汗那個動畫實現的方法中設置一個indexPath(用于外界傳入角標)來確定frame

class PhotoBrowserAnimator: NSObject {
    //設置角標
    var indexPath : NSIndexPath?
}

2 設置代理對象

這里寫圖片描述

//Mark: - 點擊圖片,彈出控制器
extension HomeViewController {
        //定義為私有的
        private func showPhotoBrowser( indexPath : NSIndexPath) {
        //將圖片的角標傳入
        photoBrowserAnimator.indexPath = indexPath
        //設置代理
        photoBrowserAnimator.presentDelegate = self
}

3 實現協議方法(該部分代碼需要寫的比較多,所以擴充一個方法)

—-> 3.1 獲取home中圖片具體的frame值(重點掌握 : 如何將cell的frame值轉為cell在window中的坐標值)

//MARK: - 實現協議中的方法
extension HomeViewController : PersentDelegate {
    func homeRect(indexPath: NSIndexPath) -> CGRect {
        //取出cell 
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))!
        //將cell的frame值轉成cell在window中的坐標
        let homeFrame = collectionView!.convertRect(cell.frame, toCoordinateSpace: UIApplication.sharedApplication().keyWindow!)
        //返回具體的尺寸
        return homeFrame
    }
}

—-> 3.2 返回一張作為轉場動畫的臨時圖片(在同一個方法中書寫)

func imageView(indexPath: NSIndexPath) -> UIImageView {
        //創建imageView對象
        let imageView = UIImageView()
        //取出cell
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))! as! HomeViewCell
        //取出cell中顯示的圖片
        let image = cell.imageView.image
        //設置圖片
        imageView.image = image
        //設置imageView相關屬性(拉伸模式)
        imageView.contentMode = .ScaleAspectFill
        //將多余的部分裁剪
        imageView.clipsToBounds = true
        //返回圖片
        return imageView
    }

—-> 3.3 返回最終展示圖片的frame值

func photoBrowserRect(indexPath: NSIndexPath) -> CGRect {
        //取出cell
        let cell = (collectionView?.cellForItemAtIndexPath(indexPath))! as! HomeViewCell
        //取出cell中顯示的圖片
        let image = cell.imageView.image
        //計算方法后的圖片的frame
        return calculate(image!)
    }

—-> 3.4 如何計算根據傳入的圖片值,得出最終展示的圖片值? 解答如下

4 創建一個類,內部只是提供一個計算展示圖片的frame(外界也是可以調用的)

這里寫圖片描述

—-> 4.1 類中代碼 :

import UIKit

//MARK: - 設置圖片的frame func calculate (image : UIImage) ->CGRect { let imageViewX : CGFloat = 0.0 let imageViewW : CGFloat = UIScreen.mainScreen().bounds.width let imageViewH : CGFloat = imageViewW / image.size.width image.size.height let imageViewY : CGFloat = (UIScreen.mainScreen().bounds.height - imageViewH) 0.5

return CGRect(x: imageViewX, y: imageViewY, width: imageViewW, height: imageViewH)

}</pre>

七 協議(消失動畫)

1 和彈出動畫一樣的道理,但是該部分只需要獲取當前展示圖片的indexPath和imageView

2 明確誰可以提供這部分需求;將代理設置給誰?代碼書寫的位置?

3 定義協議 : (在轉場動畫的類中實現協議方法定義)

//MARK: - 定義協議來實現消失動畫
protocol DismissDelegate : class {
    //獲取當前顯示的圖片的角標
    func currentIndexPath() ->NSIndexPath
    //獲取imageView(用這張臨時的圖片來實現動畫效果)
    func imageView() ->UIImageView
}

4 設置代理

class PhotoBrowserAnimator: NSObject {
    //設置消失動畫的代理
    weak var dismissDelegate : DismissDelegate?
}

5 消失動畫

—-> 5.1 獲取當前展示圖片位置

—-> 5.2 獲取imageView

—-> 5.3 根據用戶滑動后的角標,來顯示最終消失動畫后圖片在home的位置(用presentDelegate代理)

—-> 5.4 代碼 :

//消失
    func animateForDismissed (transitionContext: UIViewControllerContextTransitioning) {
        //1 取出消失的view
        let dismissView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
        dismissView.removeFromSuperview()
        //2 執行動畫
        //2.1 校驗(如果沒有值,就直接返回)
        guard let dismissDelegate = dismissDelegate else {
            return
        }
        guard let presentDelegate = presentDelegate else {
            return
        }
        // 2.2獲取一張圖片
        let imageView = dismissDelegate.imageView()
        // 2.3將圖片添加到父控件中
        transitionContext.containerView()?.addSubview(imageView)
        // 2.4 獲取當前正在顯示的圖片的角標
        let indexPath = dismissDelegate.currentIndexPath()
        // 2.5 設置起始位置
        imageView.frame = presentDelegate.photoBrowserRect(indexPath)
        //執行動畫
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
           imageView.frame = presentDelegate.homeRect(indexPath)
            }) { (_) -> Void in
            //完成動畫
            transitionContext.completeTransition(true)
        }
    }
}

6 設置代理(因為創建PhotoBrowserController是在HomeViewController類中創建的)

//Mark: - 點擊圖片,彈出控制器
extension HomeViewController {
        //定義為私有的
        private func showPhotoBrowser( indexPath : NSIndexPath) {
        //創建控制器對象
        let showPhotoBrowserVC = PhotoBrowserController()
        //設置dismiss的代理
        photoBrowserAnimator.dismissDelegate = showPhotoBrowserVC
}

—-> 6.1 明確只有展示圖片的控制器才能提供這些需求,所以將其設置為代理

7 實現代理方法

這里寫圖片描述

—-> 7.1 根據cell獲取當前的indexPath值

//Mark: - 實現dismiss的代理方法
extension PhotoBrowserController : DismissDelegate {
    func currentIndexPath() -> NSIndexPath {
        //獲取當前正在顯示的cell
        let cell = collectionView.visibleCells().first!
        //根據cell獲取indexPath
        return collectionView.indexPathForCell(cell)!
    }
}    

—-> 7.2 返回一張圖片(和7.1寫在同一個方法中)

func imageView() -> UIImageView {
        //創建UIImageView對象
        let imageView = UIImageView()
        //獲取正在顯示的cell
        let cell = collectionView.visibleCells().first! as! PhotoBrowerViewCell
        //取出cell中的圖片
        imageView.image = cell.imageView.image
        //設置圖片的屬性
        imageView.contentMode = .ScaleAspectFill
        //將多出的圖片裁剪
        imageView.clipsToBounds = true
        //返回圖片
        return imageView
    }

八 細節處理

1 在彈出動畫中點擊home中的圖片通過轉場動畫成大圖,那么會發現在圖片變大的過程中home背景并沒有消失,而是完全展示了大圖才消失的.怎么解決?

—-> 1.1 通過在動畫的時候將背景顏色設置為黑色,遮住home

//設置彈出動畫的時候背景顏色為黑色
        transitionContext.containerView()?.backgroundColor = UIColor.blackColor()

—-> 1.2 等動畫完成后再講顏色設置為clearColor

transitionContext.containerView()?.backgroundColor = UIColor.clearColor()

2 一定不要忘記轉場動畫完成后需要告訴系統轉場動畫完成,否則會出現莫名其妙的情況.

3 盡量的要對代理是否有值進行校驗,但是也可以通過強制解包(不是很安全),還是通過判斷安全點.

九 總結

1 該部分比較有難度,是對上一篇轉場動畫的加強.如果有看不懂的讀者也是很正常,有什么問題可以直接給我留言,我會盡可能的解答.

2 代碼可能考慮的還不是很完善,我只是盡量的去完成.最后如果大家覺得我寫得還行,麻煩大家關注我的官方博客,謝謝!!!!

來自: http://blog.csdn.net/xf931456371/article/details/51288824

 本文由用戶 SamShackelf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!