UISlider动画运行时获取当前值异常,点击仅返回最大值如何解决?
解决UISlider动画中点击无法获取当前值的问题
我来帮你搞定这个问题!你遇到的情况是因为两种动画机制冲突导致的,滑块的实际值其实已经直接跳到最大值了,只是视觉上还在播放动画,所以点击屏幕时只能拿到最终的max值。
问题根源分析
你原来的代码同时混用了UIView.animate和UISlider自带的setValue(_:animated:)动画:
func animateSlider(){ slider.value = Float(min) UIView.animate(withDuration: 2.0, animations: { self.slider.setValue(Float(self.max), animated:true) }) }
这里的问题在于:
slider.setValue(_:animated:true)是UISlider内部的动画逻辑,它会直接将滑块的value属性设置为目标值,只是通过内部动画让视觉上的thumb缓慢移动。- 外层的
UIView.animate试图对这个过程做包装,但两者的动画系统不兼容,最终导致滑块的value在动画开始时就被设置成了max,而非随着动画进度逐步更新。
两种可行的解决方案
方案一:用CADisplayLink实时更新滑块值
这种方法通过跟踪动画时间进度,手动计算并更新滑块的value,确保值和视觉进度完全同步,点击时能拿到当前的真实值。
代码示例:
import UIKit class YourViewController: UIViewController { @IBOutlet weak var slider: UISlider! private let minValue: CGFloat = 0 private let maxValue: CGFloat = 100 private var displayLink: CADisplayLink? private var animationStartTime: TimeInterval = 0 private let animationDuration: TimeInterval = 2.0 func animateSlider() { // 初始化滑块起始值 slider.value = Float(minValue) // 记录动画开始的时间戳 animationStartTime = CACurrentMediaTime() // 创建并启动DisplayLink,每帧更新一次滑块值 displayLink = CADisplayLink(target: self, selector: #selector(updateSliderProgress)) displayLink?.add(to: .main, forMode: .common) } @objc private func updateSliderProgress() { let elapsedTime = CACurrentMediaTime() - animationStartTime // 计算当前动画进度,确保不超过100% let progress = min(elapsedTime / animationDuration, 1.0) // 根据进度计算当前滑块值 let currentValue = minValue + (maxValue - minValue) * CGFloat(progress) // 无动画更新滑块值,保证视觉连续 slider.setValue(Float(currentValue), animated: false) // 动画结束后停止DisplayLink if progress >= 1.0 { displayLink?.invalidate() displayLink = nil } } // 点击屏幕获取当前值 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) print("当前滑块值:\(slider.value)") } }
方案二:用UIViewPropertyAnimator精确控制动画
UIViewPropertyAnimator是苹果提供的更灵活的动画控制器,它允许你随时获取动画进度,进而计算出当前的滑块值。
代码示例:
import UIKit class YourViewController: UIViewController { @IBOutlet weak var slider: UISlider! private let minValue: CGFloat = 0 private let maxValue: CGFloat = 100 private var animator: UIViewPropertyAnimator? func animateSlider() { slider.value = Float(minValue) // 创建线性动画,时长2秒 animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear) { self.slider.value = Float(self.maxValue) } // 启动动画 animator?.startAnimation() } // 点击屏幕获取当前值 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let animator = animator, animator.isRunning else { print("当前滑块值:\(slider.value)") return } // 通过动画进度计算当前值 let progress = animator.fractionComplete let currentValue = minValue + (maxValue - minValue) * CGFloat(progress) print("当前滑块值:\(Float(currentValue))") } }
总结
两种方案都能解决你的问题:
- 方案一的优势是完全掌控每一步的滑块值,适合需要精细控制的场景;
- 方案二更简洁,利用系统提供的动画控制器,代码量更少。
内容的提问来源于stack exchange,提问作者laaasBIGL
相关产品推荐
相关产品推荐

