如何根据内容高度计算自定义BottomSheetController的动画时长?
动态计算BottomSheet动画时长的优化方案
要让不同高度的BottomSheet动画都保持流畅,核心是保证动画的视觉移动速度相对恒定——单纯按高度比例缩放时长会导致矮Sheet闪速弹出、高Sheet拖沓缓慢的问题。结合你的需求,优化实现如下:
优化后的动画代码
internal class PresentAnimation: NSObject, UIViewControllerAnimatedTransitioning { // 基础移动速度(单位:pt/秒),可根据手感调整 private let baseAnimationSpeed: CGFloat = 600 // 时长边界值,避免极端情况 private let minDuration: TimeInterval = 0.2 private let maxDuration: TimeInterval = 0.5 func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { guard let toVC = context?.viewController(forKey: .to) else { return minDuration } // 直接用你设置的preferredContentSize获取准确高度 let sheetHeight = toVC.preferredContentSize.height // 按「移动距离/速度」计算基础时长 let baseDuration = TimeInterval(sheetHeight / baseAnimationSpeed) // 限制时长在合理区间内 return max(minDuration, min(baseDuration, maxDuration)) } func animateTransition(using context: UIViewControllerContextTransitioning) { guard let toView = context.view(forKey: .to), let toVC = context.viewController(forKey: .to) else { context.completeTransition(false) return } let finalFrame = context.finalFrame(for: toVC) // 初始位置:完全隐藏在屏幕底部外 var startFrame = finalFrame startFrame.origin.y = context.containerView.frame.height toView.frame = startFrame context.containerView.addSubview(toView) UIView.animate( withDuration: transitionDuration(using: context), delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseInOut, animations: { toView.frame = finalFrame }, completion: { finished in context.completeTransition(finished) } ) } }
关键优化点
- 速度优先的时长计算:用Sheet高度除以固定速度得到基础时长,确保不同高度的Sheet移动视觉速度一致,流畅度统一。
- 时长边界限制:通过最小/最大时长,避免矮Sheet动画过快(如高度100pt时,计算时长0.17秒会被拉到0.2秒)、高Sheet动画过慢(如高度400pt时,计算时长0.67秒会被限制到0.5秒)。
- 精准获取高度:直接读取控制器的
preferredContentSize,比取view的frame更准确,避免布局未完成时的尺寸误差。
额外建议
如果你的BottomSheet支持拖拽交互,可以把动画时长范围和拖拽阻尼参数配合调整,让弹出动画和拖拽手感更统一。
内容的提问来源于stack exchange,提问作者user2168735
相关产品推荐
相关产品推荐

