由于这是 Google 上的最高结果,我想我会分享我认为最理智的方式;这是使用 iOS 7+ 转换 API。我用 Swift 3 为 iOS 10 实现了这个。
如果您创建UINavigationController 的子类并返回符合UIViewControllerAnimatedTransitioning 协议的类的实例,那么将其与UINavigationController 在两个视图控制器之间的动画结合起来非常简单。
例如这里是我的UINavigationController 子类:
class NavigationController: UINavigationController {
init() {
super.init(nibName: nil, bundle: nil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension NavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return NavigationControllerAnimation(operation: operation)
}
}
您可以看到我将UINavigationControllerDelegate 设置为自身,并在我的子类的扩展中实现UINavigationControllerDelegate 中的方法,该方法允许您返回自定义动画控制器(即NavigationControllerAnimation)。此自定义动画控制器将为您替换库存动画。
您可能想知道为什么我通过其初始化程序将操作传递给NavigationControllerAnimation 实例。我这样做是为了在NavigationControllerAnimation 的UIViewControllerAnimatedTransitioning 协议的实现中我知道操作是什么(即“推送”或“弹出”)。这有助于了解我应该做什么样的动画。大多数时候,您希望根据操作执行不同的动画。
其余的都很标准。在UIViewControllerAnimatedTransitioning 协议中实现两个必需的功能,并随心所欲地制作动画:
class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {
let operation: UINavigationControllerOperation
init(operation: UINavigationControllerOperation) {
self.operation = operation
super.init()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
let containerView = transitionContext.containerView
if operation == .push {
// do your animation for push
} else if operation == .pop {
// do your animation for pop
}
}
}
重要的是要记住,对于每种不同类型的操作(即“推送”或“弹出”),to 和 from 视图控制器将是不同的。当您进行推送操作时,视图控制器将是被推送的控制器。当您处于弹出操作时,to 视图控制器将是被转换到的控制器,而 from 视图控制器将是被弹出的控制器。
另外,to 视图控制器必须作为 containerView 的子视图添加到转换上下文中。
动画完成后,您必须调用transitionContext.completeTransition(true)。如果您正在执行交互式过渡,则必须动态地将 Bool 返回到 completeTransition(didComplete: Bool),具体取决于过渡是否在动画结束时完成。
最后(可选阅读),您可能想看看我是如何完成我正在处理的转换的。这段代码有点hacky,我写得很快,所以我不会说它是很棒的动画代码,但它仍然展示了如何做动画部分。
我的过渡非常简单;我想模仿 UINavigationController 通常所做的相同动画,但我想在新视图的同时实现旧视图控制器的 1:1 动画,而不是“顶部的下一页”动画控制器出现。这样可以使两个视图控制器看起来好像是相互固定的。
对于推送操作,首先需要将toViewController的视图原点设置在屏幕外的x轴上,将其添加为containerView的子视图,通过将origin.x设置为将其动画到屏幕上零。同时,我将fromViewController 的视图设置为远离屏幕的动画:
toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
toViewController.view.frame = containerView.bounds
fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
pop 操作基本上是相反的。将toViewController 添加为containerView 的子视图,并将fromViewController 移动到右侧,就像您在左侧的toViewController 中设置动画一样:
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
toViewController.view.frame = containerView.bounds
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
这是整个 swift 文件的要点:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be