擦洗时跳转UISlider - 将UISlider与AVPlayer一起使用

时间:2017-01-20 04:13:12

标签: ios swift avplayer uislider

我正在使用AvPlayer,并尝试设置滑块以允许清理音频文件。我有一个问题,滑块跳过它选择的地方。然后它返回到原点位置一秒钟,然后返回到它被拖动的位置。

你无法看到我的光标在Gif上,但平滑的细长拖动是我移动旋钮,然后快速的鞭子是滑块行为不端。

enter image description here

我花了几个小时在谷歌搜索和梳理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
    }

}

4 个答案:

答案 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个函数:addObserversremoveObservers并在需要时调用它们

答案 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)

请确保执行以下操作:

    滑块的
  1. isContinuous 设置为 NOT 为false。
  2. 暂停玩家,然后再寻找。
  3. 寻找位置并使用完成处理程序以恢复播放。

示例代码:

@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()
        }
    })
}