和前一章类似,仅仅是代理方法的不同。
主要用到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
}
实现效果: