能否控制NSViewController转场的时长与计时函数?是否需其他方案?
关于NSViewController转场自定义时长与计时函数的解决方案
首先直接给结论:你当前使用的transition(from:to:options:completionHandler:)方法本身不支持自定义转场时长和计时函数。这个方法是苹果封装好的快速转场工具,只能通过NSViewController.TransitionOptions里的预设选项(比如淡入淡出、滑动方向等)来控制转场效果,所有动画的时长和计时曲线都是系统固定的,没有暴露参数让你修改。
不过别担心,你的需求完全可以实现,只需要切换到自定义视图控制器转场方案,通过NSViewControllerAnimatedTransitioning协议来实现完全的动画控制。下面是具体的实现步骤和示例:
核心实现思路
自定义转场需要两个核心部分:
- 一个动画控制器,遵循
NSViewControllerAnimatedTransitioning协议,负责定义转场时长、计时函数和具体的动画逻辑。 - 转场发起方的视图控制器,遵循
NSViewControllerTransitioningDelegate协议,告诉系统使用你自定义的动画控制器。
步骤1:创建自定义动画控制器
这个类是你控制转场细节的核心,你可以在这里指定任意时长、自定义计时函数(比如缓入缓出、弹性效果等),甚至完全自定义动画逻辑。
import AppKit class CustomTransitionAnimator: NSObject, NSViewControllerAnimatedTransitioning { // 自定义转场时长,这里设为0.8秒(你可以改成任意值) func transitionDuration(using transitionContext: NSViewControllerContextTransitioning?) -> TimeInterval { return 0.8 } func animateTransition(using transitionContext: NSViewControllerContextTransitioning) { // 获取转场前后的视图控制器 guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else { transitionContext.completeTransition(false) return } let containerView = transitionContext.containerView // 将目标视图添加到容器视图,初始设为透明 containerView.addSubview(toVC.view) toVC.view.alpha = 0.0 // 自定义计时函数:这里用系统预设的缓入缓出,你也可以用贝塞尔曲线创建自定义曲线 let timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // 如果你需要自定义曲线,比如: // let customTiming = CAMediaTimingFunction(controlPoints: 0.2, 0.0, 0.1, 1.0) // 执行转场动画 NSAnimationContext.runAnimationGroup({ context in context.duration = self.transitionDuration(using: transitionContext) context.timingFunction = timingFunction // 这里可以添加任意动画逻辑,比如缩放、平移+淡入淡出等 toVC.view.alpha = 1.0 fromVC.view.alpha = 0.0 }, completionHandler: { // 必须告诉系统转场已完成(包括取消的情况) transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) } }
步骤2:设置转场代理
在发起转场的视图控制器中,遵循NSViewControllerTransitioningDelegate协议,指定使用你的自定义动画控制器:
class YourSourceViewController: NSViewController, NSViewControllerTransitioningDelegate { // 示例:触发转场的方法 func triggerTransition() { let targetVC = YourTargetViewController() // 设置转场代理 targetVC.transitioningDelegate = self // 发起转场(present方式,如果你是用addChild的方式,逻辑类似) present(targetVC, animated: true) } // MARK: - NSViewControllerTransitioningDelegate func animationController(forPresented presented: NSViewController, presenting: NSViewController, source: NSViewController) -> NSViewControllerAnimatedTransitioning? { // 返回自定义动画控制器用于present return CustomTransitionAnimator() } func animationController(forDismissed dismissed: NSViewController) -> NSViewControllerAnimatedTransitioning? { // 返回自定义动画控制器用于dismiss(可以和present用同一个,也可以单独写一个反向动画) return CustomTransitionAnimator() } }
额外说明
如果你只是想在系统默认转场的基础上附加一些自定义动画(而不是完全替换系统转场),可以使用NSViewControllerTransitionCoordinator的animate(alongsideTransition:completion:)方法,但这种方式无法修改系统原转场的时长和计时函数,只能叠加动画。
比如:
self.transition(from: oldVC, to: newVC, options: .crossfade, completionHandler: nil) self.transitionCoordinator?.animate(alongsideTransition: { context in // 这里添加你要叠加的动画,比如newVC.view的缩放效果 newVC.view.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) }, completionHandler: { context in newVC.view.transform = .identity })
但如果你的核心需求是控制转场的时长和计时函数,那么自定义转场协议是唯一的可行方案。
内容的提问来源于stack exchange,提问作者Ky -
相关产品推荐
相关产品推荐

