和前一章類似,僅僅是代理方法的不同。
主要用到UINavigationControllerDelegate的代理方法navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController),該代理方法也是需要返回一個UIViewControllerAnimatedTransitioning的可選對象,當返回nil時,UIKit將使用內置系統默認動畫,當不爲nil時,將使用你自己自定義的動畫效果。
現在有這樣一個場景
我們來創建一個動畫構造器
class RevealAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let animationDuration = 2.0
var operation: UINavigationControllerOperation = .push
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
}
}
然後在MasterViewController.swift的viewDidLoad中配置代理
navigationController?.delegate = self
實現代理方法
extension MasterViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.operation = operation
return transition
}
}
現在我們來實現動畫構造器RevealAnimator中的方法。
因爲需要創建一些layer動畫,我們先存一下動畫上下文。
weak var storedContext: UIViewControllerContextTransitioning?
然後在animateTransition()中添加初始化轉場代碼
storedContext = transitionContext
if operation == .push {
let fromVC = transitionContext.viewController(forKey: .from) as! MasterViewController
let toVC = transitionContext.viewController(forKey: .to) as! DetailViewController
transitionContext.containerView.addSubview(toVC.view)//將toView添加到容器當中
toVC.view.frame = transitionContext.finalFrame(for: toVC)//設置toView的座標
...
}
現在我們添加一個放大logo切換場景的動畫
let animation = CABasicAnimation(keyPath: "transform")
animation.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
animation.toValue = NSValue(caTransform3D: CATransform3DConcat(CATransform3DMakeTranslation(0.0, -10.0, 0.0), CATransform3DMakeScale(150.0, 150.0, 1.0)))
animation.duration = animationDuration
animation.delegate = self
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
let maskLayer: CAShapeLayer = RWLogoLayer.logoLayer()
maskLayer.position = fromVC.logo.position
toVC.view.layer.mask = maskLayer
maskLayer.add(animation, forKey: nil)
fromVC.logo.add(animation, forKey: nil)
運行效果
爲了移除fromVC的logo,同樣也添加一個動畫
fromVC.logo.add(animation, forKey: nil)
動畫結束時,我們需要設置轉場動畫完成,並移除fromVC的所有動畫,再將toVC的mask清空
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
context.completeTransition(!context.transitionWasCancelled)
let fromVC = context.viewController(forKey: .from) as! MasterViewController
fromVC.logo.removeAllAnimations()
let toVC = context.viewController(forKey: .to) as! DetailViewController
toVC.view.layer.mask = nil
}
storedContext = nil
}
實現效果: