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

Pip*_*ppo 5 uislider ios avplayer swift

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

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

在此处输入图片说明

我花了几个小时在谷歌上搜索和梳理 Stack Overflow,但无法弄清楚我在这里做错了什么,很多类似的问题都很老而且在 ObjC 中。

这是我认为导致问题的代码部分,它确实处理了滑块被移动的事件:我也尝试过没有 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我将在下面包括我的整个班级以供参考。

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
    }

}
Run Code Online (Sandbox Code Playgroud)

Ked*_*kar 6

斯威夫特 5、Xcode 11

我遇到了同样的问题,显然是 periodicalTimeObserver 导致返回不正确的时间,从而导致滑块滞后或跳跃。我通过在滑块更改时删除周期性时间观察器并在调用寻求完成处理程序时将其添加回来来解决这个问题。

@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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Moh*_*hyG 5

您需要在用户选择滑块上的拇指后立即删除观察者并使计时器无效,并在拖动完成后再次将其添加回来

在加载播放器的地方添加这样的目标:

    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 )
Run Code Online (Sandbox Code Playgroud)

并删除观察者并使计时器无效然后mySliderBeganTracking添加观察者mySliderEndedTracking 以更好地控制播放器中发生的情况编写 2 个函数:addObserversremoveObservers在需要时调用它们