自定義modal動畫 by Swift

主要內(nèi)容:實(shí)現(xiàn)自定義轉(zhuǎn)場動畫


大家平常用微信,微博的過程中肯定都有查看過朋友圈和微博所發(fā)布的照片,當(dāng)點(diǎn)擊九宮格的某一圖片時(shí)圖片會慢慢的放大并進(jìn)入全屏,左右滑動查看另一張.輕點(diǎn)圖片又會以動畫的方式慢慢縮小回到滑動之后對應(yīng)的圖片.直接上圖和代碼吧,talk is cheap.
  • 先展示一下效果
gif1
gif1

gif2
gif2

  • 具體實(shí)現(xiàn)步驟

    • 1 .創(chuàng)建兩個(gè)控制器,一個(gè)是HomeViewController(modal之前的控制器),九宮格展示圖片列表,另一個(gè)是PhotoViewController(之后需要modal出來的控制器),用于展示大圖.
    • 2 .自定義一個(gè)類(可以定義成工具類,方便復(fù)用),需要繼承自NSObject并遵守UIViewControllerTransitioningDelegate協(xié)議,并且實(shí)現(xiàn)該協(xié)議中的兩個(gè)代理方法,目的是為了設(shè)置管理顯示和消失動畫是由哪個(gè)控制器管理,isPresented屬性用于之后的判斷是消失動畫還是顯示動畫.
    class TransionAnimator: NSObject {
    // MARK:- 設(shè)置屬性
    var isPresented : Bool = false
    var indexPath : NSIndexPath?
    

}
// 設(shè)置管理出現(xiàn)動畫
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?{

    isPresented = true
    return self
}

// 設(shè)置管理消失動畫
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    isPresented = false
    return self
}
```

- 3 .在`HomeViewController`中去定義一個(gè)屬性用來保存轉(zhuǎn)場動畫管理者,因?yàn)槿绻搶ο蟊会尵蜔o法完成轉(zhuǎn)場動畫.并且將這個(gè)對象設(shè)置成為`PhotoViewController`的`transitioningDelegate`,并且實(shí)現(xiàn)`UIViewControllerAnimatedTransitioning`協(xié)議中的方法,才能自定義轉(zhuǎn)場動畫

  • 4 .來到自定義的動畫管理者文件中,實(shí)現(xiàn)協(xié)議中的方法

    //設(shè)置動畫持續(xù)時(shí)間
    public func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
    //設(shè)置具體的動畫
    public func animateTransition(transitionContext: UIViewControllerContextTransitioning)
    

  • 5 如何去實(shí)現(xiàn)動畫:

    • 5.1 考慮到完成顯示和消失的動畫需要三個(gè)值,起始的位置,結(jié)束的位置,還有執(zhí)行動畫的占位視圖.但是這三個(gè)值并不是固定的,需要外界傳入.這時(shí)就考慮面向協(xié)議開發(fā),就像許多第三方框架所做的:只需要調(diào)用提供的接口,并傳入所需要的接口,
    // 顯示動畫代理
    

protocol TransitionPresentedProtocol : class {
func getImageView(indexPath : NSIndexPath) -> UIImageView
func getStartRect(indexPath : NSIndexPath) -> CGRect
func getEndRect(indexPath : NSIndexPath) -> CGRect
}
//消失動畫代理
protocol TransitionDismissProtocol : class {
func getImageView() -> UIImageView
func getIndexPath() -> NSIndexPath
}

```

 - 5.2 實(shí)現(xiàn)代理方法,獲得需要的值:

 ```swift
 //實(shí)現(xiàn)顯示動畫代理,獲得返回值
 extension HomeViewController : TransitionPresentedProtocol {
func getImageView(indexPath: NSIndexPath) -> UIImageView {
    let imageView = UIImageView()
    let cell = collectionView(collectionView!, cellForItemAtIndexPath: indexPath) as! HomeViewCell
    imageView.image = cell.imageView.image
    imageView.contentMode = .ScaleAspectFill
    imageView.clipsToBounds = true
    return imageView
}
//
func getEndRect(indexPath: NSIndexPath) -> CGRect {
    let cell = collectionView(collectionView!, cellForItemAtIndexPath: indexPath) as! HomeViewCell
    return calculateImageFrame(cell.imageView.image!)
}
func getStartRect(indexPath: NSIndexPath) -> CGRect {
    let cell = collectionView(collectionView!, cellForItemAtIndexPath: indexPath) as! HomeViewCell
    let startRect = collectionView!.convertRect(cell.frame, toView: UIApplication.sharedApplication().keyWindow)
    return startRect
}
 ```

 ```swift
 //實(shí)現(xiàn)消失動畫代理,獲得返回值
extension PhotoViewController : TransitionDismissProtocol {
func getImageView() -> UIImageView {
    let cell = collectionV.visibleCells().first as! PhotoViewCell
    let imageView = cell.imageV
    imageView.contentMode = .ScaleAspectFill
    imageView.clipsToBounds = true
    return imageView
}
func getIndexPath() -> NSIndexPath {
    let cell =  collectionV.visibleCells().first as! PhotoViewCell
    let indexPath = collectionV.indexPathForCell(cell)
    return indexPath!
}
 ```
  - 5.3 實(shí)現(xiàn)動畫效果

  ```swift

//設(shè)置轉(zhuǎn)場動畫時(shí)長
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 2.0
}
//設(shè)置轉(zhuǎn)場動畫
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
//設(shè)置顯示動畫
if isPresented {
//0.nil值校驗(yàn)
guard let indexPath = indexPath,presentedDelegate = presentedDelegate else {return}
//1獲得彈出的View
let presentedView = transitionContext.viewForKey(UITransitionContextToViewKey)!
//2.獲得占位視圖
let imageV = presentedDelegate.getImageView(indexPath)
//3.將占位視圖添加到containerView上
transitionContext.containerView()?.addSubview(imageV)
//4.設(shè)置占位視圖的frame
imageV.frame = presentedDelegate.getStartRect(indexPath)
//5.容器視圖設(shè)置背景色
transitionContext.containerView()?.backgroundColor = UIColor.blackColor()
//6.執(zhí)行動畫
UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
imageV.frame = presentedDelegate.getEndRect(indexPath)
}, completion: { (_) -> Void in
//移除占位視圖
imageV.removeFromSuperview()
//將彈出的View加到containerView上
transitionContext.containerView()?.addSubview(presentedView)
//容器視圖清除背景色
transitionContext.containerView()?.backgroundColor = UIColor.clearColor()
//設(shè)置已完成轉(zhuǎn)場
transitionContext.completeTransition(true)
})

    } else {//設(shè)置消失動畫
        //0.nil值校驗(yàn)
        guard let presentedDelegate = presentedDelegate , dismissDelegate = dismissDelegate  else {return}
        //1獲得即將消失的view
        let dismissView = transitionContext.viewForKey(UITransitionContextFromViewKey)!

        //3.獲得占位imageView
        let imageView = dismissDelegate.getImageView()
        //4.將占位視圖添加到容器視圖上面
        transitionContext.containerView()?.addSubview(imageView)
        //2執(zhí)行消失動畫
        //獲得最終的indexPath
        let indexPath = dismissDelegate.getIndexPath()
        //獲得之前顯示動畫的起始位置,
        var endRect = presentedDelegate.getStartRect(indexPath)

        //判斷最終消失的位置是否在屏幕之外
        if endRect.origin.y > UIScreen.mainScreen().bounds.height {
            endRect = CGRectZero
        }

        dismissView.alpha = endRect == CGRectZero ? 1.0 : 0.0
        imageView.alpha = 1.0 - dismissView.alpha

        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
            if endRect == CGRectZero {
                dismissView.alpha = 0.0
            } else {
                imageView.frame = endRect
            }

        }, completion: { (_) -> Void in
                imageView.removeFromSuperview()
                dismissView.removeFromSuperview()
                transitionContext.completeTransition(true)
        })
    }
}
```
- 6 注意事項(xiàng):

 - 如果在PhotoView里一直向后滑動,那么返回之前的HomeView的時(shí)候可能會導(dǎo)致崩潰.這是由于cell的重用機(jī)制,只有當(dāng)cell即將顯示出來的時(shí)候才會去緩存池中取cell并加載,若HomeView中的cell超出了屏幕的范圍,是取不出cell的,從而導(dǎo)致崩潰.那么解決方法可以時(shí)主動去調(diào)用數(shù)據(jù)源方法,傳入indexPath返回一個(gè)cell,再去使用cell里面的數(shù)據(jù),或者使用設(shè)置offset的方式,滾動到對應(yīng)位置,就能取出對應(yīng)的cell了. 我使用的是主動去調(diào)用
 - 

![Uploading photo1_989090.gif . . .]transitionContext.containerView,也就是容器層是用來展示最后modal出來的View,要把占位視圖和最后modal出來的View添加上去


代碼地址

下載之后pod install,打開.xcworkspace就能運(yùn)行
https://git.coding.net/DHai/PhotoViewer-Swift.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顷编,一起剝皮案震驚了整個(gè)濱河市抚官,隨后出現(xiàn)的幾起案子菩鲜,更是在濱河造成了極大的恐慌,老刑警劉巖胳螟,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔馋,死亡現(xiàn)場離奇詭異筹吐,居然都是意外死亡糖耸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門丘薛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘉竟,“玉大人,你說我怎么就攤上這事洋侨∩崛牛” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵希坚,是天一觀的道長边苹。 經(jīng)常有香客問我,道長裁僧,這世上最難降的妖魔是什么个束? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮聊疲,結(jié)果婚禮上茬底,老公的妹妹穿的比我還像新娘。我一直安慰自己获洲,他們只是感情好阱表,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般最爬。 火紅的嫁衣襯著肌膚如雪涉馁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天烂叔,我揣著相機(jī)與錄音谨胞,去河邊找鬼。 笑死蒜鸡,一個(gè)胖子當(dāng)著我的面吹牛胯努,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逢防,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叶沛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忘朝?” 一聲冷哼從身側(cè)響起灰署,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎局嘁,沒想到半個(gè)月后溉箕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悦昵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年肴茄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片但指。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寡痰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棋凳,到底是詐尸還是另有隱情拦坠,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布剩岳,位于F島的核電站贞滨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拍棕。R本人自食惡果不足惜晓铆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莫湘。 院中可真熱鬧尤蒿,春花似錦、人聲如沸幅垮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至示弓,卻和暖如春讳侨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奏属。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工跨跨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囱皿。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓勇婴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘱腥。 傳聞我的和親對象是個(gè)殘疾皇子耕渴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容