为 AVPlayer 更新时 UISlider 跳转
UISlider jumps when updating for AVPlayer
我尝试用 UISlider 实现简单的播放器来指示当前音频文件的时间。
在代码中我添加了两个观察者:
slider.rx.value.subscribe(onNext: { value in
let totalTime = Float(CMTimeGetSeconds(self.player.currentItem!.duration))
let seconds = value * totalTime
let time = CMTime(seconds: Double(seconds), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
self.player.seek(to: time)
}).disposed(by: bag)
let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
player.addPeriodicTimeObserver(forInterval: interval, queue: nil) { [weak self] time in
self?.updateSlider(with: time)
}
具有一个私有函数:
private func updateSlider(with time: CMTime) {
let currentTime = CMTimeGetSeconds(time)
var totalTime = CMTimeGetSeconds(player.currentItem!.duration)
if totalTime.isNaN {
totalTime = 0
}
startLabel.text = Int(currentTime).descriptiveDuration
endLabel.text = Int(totalTime).descriptiveDuration
slider.value = Float(currentTime / totalTime)
}
当音频播放时,一切正常,滑块几乎更新。当我尝试在播放音频时手动移动滑块时出现问题,然后它跳了。为什么?
更新:
其实我知道为什么。因为我更新了两次:手动和玩家观察者,但如何防止这种行为?我不知道;)请帮忙。
解决此问题的一个简单方法是防止 addPeriodicTimeObserver
在触摸滑块时调用 self?.updateSlider(with: time)
。
这可以通过 UISlider
s isTracking
属性:
来确定
isTracking
A Boolean value indicating whether the control is currently tracking
touch events.
While tracking of a touch event is in progress, the control sets the
value of this property to true. When tracking ends or is cancelled for
any reason, it sets this property to false.
Ref: https://developer.apple.com/documentation/uikit/uicontrol/1618210-istracking
这存在于所有 UIControl
个元素中,您可以这样使用:
player.addPeriodicTimeObserver(forInterval: interval, queue: nil) { [weak self] time in
//check if slider is being touched/tracked
guard self?.slider.isTracking == false else { return }
//if slider is not being touched, then update the slider from here
self?.updateSlider(with: time)
}
一般示例:
@IBOutlet var slider: UISlider!
//...
func startSlider() {
slider.value = 0
slider.maximumValue = 10
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] (timer) in
print("Slider at: \(self?.slider.value)")
guard self?.slider.isTracking == false else { return }
self?.updateSlider(to: self!.slider.value + 0.1)
}
}
private func updateSlider(to value: Float) {
slider.value = value
}
我确定还有其他(更好)的方法,但我在 RxSwift 方面(还)做得不多。
我希望这已经足够好了。
我尝试用 UISlider 实现简单的播放器来指示当前音频文件的时间。
在代码中我添加了两个观察者:
slider.rx.value.subscribe(onNext: { value in
let totalTime = Float(CMTimeGetSeconds(self.player.currentItem!.duration))
let seconds = value * totalTime
let time = CMTime(seconds: Double(seconds), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
self.player.seek(to: time)
}).disposed(by: bag)
let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
player.addPeriodicTimeObserver(forInterval: interval, queue: nil) { [weak self] time in
self?.updateSlider(with: time)
}
具有一个私有函数:
private func updateSlider(with time: CMTime) {
let currentTime = CMTimeGetSeconds(time)
var totalTime = CMTimeGetSeconds(player.currentItem!.duration)
if totalTime.isNaN {
totalTime = 0
}
startLabel.text = Int(currentTime).descriptiveDuration
endLabel.text = Int(totalTime).descriptiveDuration
slider.value = Float(currentTime / totalTime)
}
当音频播放时,一切正常,滑块几乎更新。当我尝试在播放音频时手动移动滑块时出现问题,然后它跳了。为什么?
更新:
其实我知道为什么。因为我更新了两次:手动和玩家观察者,但如何防止这种行为?我不知道;)请帮忙。
解决此问题的一个简单方法是防止 addPeriodicTimeObserver
在触摸滑块时调用 self?.updateSlider(with: time)
。
这可以通过 UISlider
s isTracking
属性:
isTracking
A Boolean value indicating whether the control is currently tracking touch events.
While tracking of a touch event is in progress, the control sets the value of this property to true. When tracking ends or is cancelled for any reason, it sets this property to false.
Ref: https://developer.apple.com/documentation/uikit/uicontrol/1618210-istracking
这存在于所有 UIControl
个元素中,您可以这样使用:
player.addPeriodicTimeObserver(forInterval: interval, queue: nil) { [weak self] time in
//check if slider is being touched/tracked
guard self?.slider.isTracking == false else { return }
//if slider is not being touched, then update the slider from here
self?.updateSlider(with: time)
}
一般示例:
@IBOutlet var slider: UISlider!
//...
func startSlider() {
slider.value = 0
slider.maximumValue = 10
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] (timer) in
print("Slider at: \(self?.slider.value)")
guard self?.slider.isTracking == false else { return }
self?.updateSlider(to: self!.slider.value + 0.1)
}
}
private func updateSlider(to value: Float) {
slider.value = value
}
我确定还有其他(更好)的方法,但我在 RxSwift 方面(还)做得不多。
我希望这已经足够好了。