Swift 实现菜单扩散式转场动画(简单)

2,166 阅读2分钟

<img class="alignnone size-full wp-image-576" src="https://ios.devdon.com/wp-content/uploads/2017/03/banner.jpg" alt="" width="1024" height="576" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/banner.jpg 1024w, https://ios.devdon.com/wp-content/uploads/2017/03/banner-300x169.jpg 300w, https://ios.devdon.com/wp-content/uploads/2017/03/banner-768x432.jpg 768w" sizes="(max-width: 1024px) 100vw, 1024px" />

今天我們要來實現上面這一個簡單的轉場動畫,非常的容易實現,剛開始接觸轉場動畫的同學可以拿來練手一下。

這一篇文章將不會很仔細地說明轉場動畫的邏輯,所以如果還不了解如何自定義轉場動畫或者它的原理,
請跳轉至:Swift 自订义非交互转场动画(Custom Transition ViewController)

準備動作

我們通過實現UINavigationControllerDelegate來自定義Navigation的轉場方法,並且告訴Transition我們進行的是push還是pop,
並且告知是哪一個按鈕被點了(selectedView)

    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        
        if selectedView != nil {
            transition.selectedView = selectedView!
        }
        
        if fromVC is MenuViewController {
            transition.isPush = true
        } else {
            transition.isPush = false
        }
        
        return transition
    }

創建SKDiffusionTransition.swift來實現轉場動畫協議的方法

// 動畫時間
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

// 具體動畫內容
func animateTransition(using transitionContext: UIViewControllerContextTransitioning)

在animateTransition中準備好fromView, toView, containerView

        // from view
        let fromVC = transitionContext.viewController(forKey: .from)!
        let fromView = fromVC.view!
        
        // to view
        let toVC = transitionContext.viewController(forKey: .to)!
        let toView = toVC.view!
        
        // container view
        let containerView = transitionContext.containerView

實現Push展開動畫

<img class="alignnone wp-image-577" src="https://ios.devdon.com/wp-content/uploads/2017/03/push-transition.gif" alt="" width="236" height="407" />

當使用者點下黃色按鈕時,我們將toView從按鈕的位置展開至全畫面。

        if isPush {
            containerView.addSubview(fromView)
            
            toView.frame = selectedView.frame
            toView.clipsToBounds = true
            containerView.addSubview(toView)
            
            UIView.animate(withDuration: 2, animations: {
                toView.frame = UIScreen.main.bounds

            }, completion: { finished in
                fromView.removeFromSuperview()
                transitionContext.completeTransition(true)
                
            })
        }

實現Pop收縮動畫

<img class="alignnone wp-image-578" src="https://ios.devdon.com/wp-content/uploads/2017/03/pop-transition.gif" alt="" width="304" height="524" />

將充滿畫面的圖,縮小到之前所點的按鈕位置。

這裡是通過遮罩層(Mask)來做的,不過不太清楚的是,為什麼打算從SuperView移出maskView的時候會Crash,需要再研究下引用關係。

            let maskView = UIView(frame: fromView.frame)
            maskView.backgroundColor = UIColor.black
            maskView.clipsToBounds = true
            
            containerView.addSubview(toView)
            containerView.addSubview(maskView)
            containerView.addSubview(fromView)
            
            fromView.mask = maskView

            UIView.animate(withDuration: 0.2, animations: {
                maskView.frame = self.selectedView.frame
                maskView.layer.cornerRadius = self.selectedView.frame.width/2
                
            }, completion: { finished in
                // don't know why it will crash
//                maskView.removeFromSuperview()
                
                fromView.removeFromSuperview()
                transitionContext.completeTransition(true)
                
            })

如果在自定義轉場動畫的時候發現畫面卡住不動,看看是不是忘記使用下面的方法通知系統完成動畫了,

或者是忘記將哪一個畫面從containerView中移除了。

transitionContext.completeTransition(true)

推薦和參考