我正在使用AvPlayer,并尝试设置滑块以允许清理音频文件。我有一个问题,滑块跳过它选择的地方。然后它返回到原点位置一秒钟,然后返回到它被拖动的位置。
你无法看到我的光标在Gif上,但平滑的细长拖动是我移动旋钮,然后快速的鞭子是滑块行为不端。
我花了几个小时在谷歌搜索和梳理Stack Overflow并且无法弄清楚我在这里做错了什么,很多类似的问题在ObjC中已经很老了。
这是我认为负责问题的代码部分,它确实处理滑块被移动的事件:Ive尝试了它而没有if语句也没有看到不同的结果。
@IBAction func horizontalSliderActioned(_ sender: Any) {
horizontalSlider.isContinuous = true
if self.horizontalSlider.isTouchInside {
audioPlayer?.pause()
let seconds : Int64 = Int64(horizontalSlider.value)
let preferredTimeScale : Int32 = 1
let seekTime : CMTime = CMTimeMake(seconds, preferredTimeScale)
audioPlayerItem?.seek(to: seekTime)
audioPlayer?.play()
} else {
let duration : CMTime = (self.audioPlayer?.currentItem!.asset.duration)!
let seconds : Float64 = CMTimeGetSeconds(duration)
self.horizontalSlider.value = Float(seconds)
}
}
我将在下面列出我的全班以供参考。
import UIKit
import Parse
import AVFoundation
import AVKit
class PlayerViewController: UIViewController, AVAudioPlayerDelegate {
@IBOutlet var horizontalSlider: UISlider!
var selectedAudio: String!
var audioPlayer: AVPlayer?
var audioPlayerItem: AVPlayerItem?
var timer: Timer?
func getAudio() {
let query = PFQuery(className: "Part")
query.whereKey("objectId", equalTo: selectedAudio)
query.getFirstObjectInBackground { (object, error) in
if error != nil || object == nil {
print("The getFirstObject request failed.")
} else {
print("There is an object now get the Audio. ")
let audioFileURL = (object?.object(forKey: "partAudio") as! PFFile).url
self.audioPlayerItem = AVPlayerItem(url: NSURL(string: audioFileURL!) as! URL)
self.audioPlayer = AVPlayer(playerItem: self.audioPlayerItem)
let playerLayer = AVPlayerLayer(player: self.audioPlayer)
playerLayer.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
self.view.layer.addSublayer(playerLayer)
let duration : CMTime = (self.audioPlayer?.currentItem!.asset.duration)!
let seconds : Float64 = CMTimeGetSeconds(duration)
let maxTime : Float = Float(seconds)
self.horizontalSlider.maximumValue = maxTime
self.audioPlayer?.play()
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PlayerViewController.audioSliderUpdate), userInfo: nil, repeats: true)
}
}
}
@IBOutlet var playerButton: UIButton!
func playerButtonTapped() {
if audioPlayer?.rate == 0 {
audioPlayer?.play()
self.playerButton.setImage(UIImage(named: "play"), for: UIControlState.normal)
} else {
audioPlayer?.pause()
self.playerButton.setImage(UIImage(named: "pause"), for: UIControlState.normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
horizontalSlider.minimumValue = 0
horizontalSlider.value = 0
self.playerButton.addTarget(self, action: #selector(PlayerViewController.playerButtonTapped), for: UIControlEvents.touchUpInside)
getAudio()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(PlayerViewController.finishedPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.audioPlayerItem)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
// remove the timer
self.timer?.invalidate()
// remove the observer when leaving page
NotificationCenter.default.removeObserver(audioPlayer?.currentItem! as Any)
}
func finishedPlaying() {
// need option to play next track
self.playerButton.setImage(UIImage(named: "play"), for: UIControlState.normal)
let seconds : Int64 = 0
let preferredTimeScale : Int32 = 1
let seekTime : CMTime = CMTimeMake(seconds, preferredTimeScale)
audioPlayerItem!.seek(to: seekTime)
}
@IBAction func horizontalSliderActioned(_ sender: Any) {
horizontalSlider.isContinuous = true
if self.horizontalSlider.isTouchInside {
audioPlayer?.pause()
let seconds : Int64 = Int64(horizontalSlider.value)
let preferredTimeScale : Int32 = 1
let seekTime : CMTime = CMTimeMake(seconds, preferredTimeScale)
audioPlayerItem?.seek(to: seekTime)
audioPlayer?.play()
} else {
let duration : CMTime = (self.audioPlayer?.currentItem!.asset.duration)!
let seconds : Float64 = CMTimeGetSeconds(duration)
self.horizontalSlider.value = Float(seconds)
}
}
func audioSliderUpdate() {
let currentTime : CMTime = (self.audioPlayerItem?.currentTime())!
let seconds : Float64 = CMTimeGetSeconds(currentTime)
let time : Float = Float(seconds)
self.horizontalSlider.value = time
}
}
答案 0 :(得分:2)
一旦用户选择滑块上的滑块并在拖动完成后再将其添加回来,您需要删除观察者并使计时器无效
在你加载播放器时添加这样的目标:
mySlider.addTarget(self,
action: #selector(PlayerViewController.mySliderBeganTracking(_:)),
forControlEvents:.TouchDown)
mySlider.addTarget(self,
action: #selector(PlayerViewController.mySliderEndedTracking(_:)),
forControlEvents: .TouchUpInside)
mySlider.addTarget(self,
action: #selector(PlayerViewController.mySliderEndedTracking(_:)),
forControlEvents: .TouchUpOutside )
并移除观察者并使mySliderBeganTracking
中的计时器无效,然后在mySliderEndedTracking
中添加观察者
为了更好地控制播放器中发生的事情,可以编写2个函数:addObservers
和removeObservers
并在需要时调用它们
答案 1 :(得分:0)
尝试使用如下的时间滑块值:
@IBAction func timeSliderDidChange(_ sender: UISlider) {
AVPlayerManager.sharedInstance.currentTime = Double(sender.value)
}
var currentTime: Double {
get {
return CMTimeGetSeconds(player.currentTime())
}
set {
if self.player.status == .readyToPlay {
let newTime = CMTimeMakeWithSeconds(newValue, 1)
player.seek(to: newTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero) { ( _ ) in
self.updatePlayerInfo()
}
}
}
}
并在用户释放滑块时传递滑块的值,在滑块上发生用户交互时也不要更新当前播放的滑块值
答案 2 :(得分:0)
Swift 5,Xcode 11
我遇到了同样的问题,显然是periodicTimeObserver导致返回不正确的时间,这导致了滑块的滞后或跳跃。我通过在更改滑块时删除定期的时间观察者并在调用完成处理程序时将其添加回来解决了该问题。
@objc func sliderValueChanged(_ playbackSlider: UISlider, event: UIEvent){
let seconds : Float = Float(playbackSlider.value)
let targetTime:CMTime = CMTimeMake(value: Int64(seconds), timescale: 1)
if let touchEvent = event.allTouches?.first {
switch touchEvent.phase {
case .began:
// handle drag began
//Remove observer when dragging is in progress
self.removePeriodicTimeObserver()
break
case .moved:
// handle drag moved
break
case .ended:
// handle drag ended
//Add Observer back when seeking got completed
player.seek(to: targetTime, toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] (value) in
self?.addTimeObserver()
}
break
default:
break
}
}
}
答案 3 :(得分:0)
请确保执行以下操作:
示例代码:
@objc func sliderValueChanged(sender: UISlider, event: UIEvent) {
let roundedValue = sender.value.rounded()
guard let touchEvent = event.allTouches?.first else { return }
switch touchEvent.phase {
case .began:
PlayerManager.shared.pause()
case .moved:
print("Slider moved")
case .ended:
PlayerManager.shared.seek(to: roundedValue, playAfterSeeking: true)
default: ()
}
}
这是寻找的功能:
func seek(to: Float, playAfterSeeking: Bool) {
player?.seek(to: CMTime(value: CMTimeValue(to), timescale: 1), completionHandler: { [weak self] (status) in
if playAfterSeeking {
self?.play()
}
})
}