iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 20
0

https://ithelp.ithome.com.tw/upload/images/20180108/20107329IZ7Weo8Eyi.png
先從一個簡單的轉場動畫開始,了解自定義轉場動畫的過程。


Color Diffusion Transition

Color Diffusion Transition

第一個畫面背景是綠色的,中間有個紅色的箱子,當點下紅色的箱子以後,箱子會擴散出紅色至整個畫面。

第二個畫面背景是紅色的,中間有個綠色的箱子,當點下綠色的箱子以後,紅色的背景會縮到中間的箱子當中。

HomeViewController

點下箱子以後會去 present 第二個畫面,這裡比一般的 present 多了兩個設定,讓 ViewController 知道我們要自定義轉場動畫。

let VC = DetailViewController()
VC.modalPresentationStyle = .custom
VC.transitioningDelegate = diffustionTransition
present(VC, animated: true, completion: nil)

另外我們會告訴 diffusionTransition 我們要在哪一個 View 上做動畫(從那裡擴散和從那裡收縮)

這個畫面的例子就是中間的按鈕了。

diffustionTransition = SKDiffussionTransition(animatedView: button)

SKDiffusionTransition

繼承於 NSObject, UIViewControllerAnimatedTransitioning

在這個方法中返回動畫的 duration

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

另外會實作 UIViewControllerTransitioningDelegate 中實現兩個方法。

通過改變自己定義的 isReverse 屬性,讓 SKDiffusionTransition 知道目前是要做 present 還是 dismiss 的動畫效果。

extension SKDiffussionTransition: UIViewControllerTransitioningDelegate {
    open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isReverse = false
        return self
    }
    
    open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isReverse = true
        return self
    }
}

轉場動畫的實現

具體的轉場動畫內容都會在這個方法中實現

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        // get frame and backgroundColor
        var startFrame = CGRect.zero
        if animatedView != nil {
            startFrame = animatedView!.frame
            startBackgroundColor = animatedView!.backgroundColor
        }
        
        // init animated view for transition
        let animatedViewForTransition = UIView(frame: startFrame)
        animatedViewForTransition.clipsToBounds = true
        animatedViewForTransition.layer.cornerRadius = animatedViewForTransition.frame.height / 2.0
        animatedViewForTransition.backgroundColor = self.startBackgroundColor
        
        // add animated view on transitionContext's containerView
        transitionContext.containerView.addSubview(animatedViewForTransition)
        
        // set presentedController
        let presentedController: UIViewController
        if !isReverse {
            presentedController = transitionContext.viewController(forKey: .to)!
            presentedController.view.layer.opacity = 0
        } else {
            presentedController = transitionContext.viewController(forKey: .from)!
        }
        
        presentedController.view.frame = transitionContext.containerView.bounds
        transitionContext.containerView.addSubview(presentedController.view)
        
        let size = max(transitionContext.containerView.frame.height, transitionContext.containerView.frame.width) * 1.2
        let scaleFactor = size / animatedViewForTransition.frame.width
        let finalTransform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
        
        
        if !self.isReverse {
            UIView.transition(with: animatedViewForTransition,
                              duration: self.transitionDuration(using: transitionContext) * 0.7,
                              options: [],
                              animations: {
                                animatedViewForTransition.transform = finalTransform
                                animatedViewForTransition.center = transitionContext.containerView.center
                                animatedViewForTransition.backgroundColor = presentedController.view.backgroundColor
            },completion: nil)
            
            UIView.animate(withDuration: self.transitionDuration(using: transitionContext) * 0.4,
                           delay: self.transitionDuration(using: transitionContext) * 0.6,
                           animations: {
                            presentedController.view.layer.opacity = 1
            },completion: { (_) in
                animatedViewForTransition.removeFromSuperview()
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            })
            
        } else {
            animatedViewForTransition.transform = finalTransform
            animatedViewForTransition.center = transitionContext.containerView.center
            animatedViewForTransition.backgroundColor = presentedController.view.backgroundColor
            
            UIView.animate(withDuration: self.transitionDuration(using: transitionContext) * 0.7, animations: {
                presentedController.view.layer.opacity = 0
            })
            
            DispatchQueue.main.asyncAfter(deadline: .now() + self.transitionDuration(using: transitionContext) * 0.3) {
                UIView.transition(with: animatedViewForTransition,
                                  duration: self.transitionDuration(using: transitionContext) * 0.6,
                                  options: [],
                                  animations: {
                                    animatedViewForTransition.transform = CGAffineTransform.identity
                                    animatedViewForTransition.backgroundColor = self.startBackgroundColor
                                    animatedViewForTransition.frame = startFrame
                },
                                  completion: { (_) in
                                    animatedViewForTransition.removeFromSuperview()
                                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                })
            }
        }
    }
    
}

Reference


上一篇
Snapshot - 轉場動畫的暖身 (Transition)
下一篇
Expand Style Transition - 展開式轉場動畫
系列文
iOS Swift x Layout x Animation x Transition30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言