在 iOS 中播放 Avplayer 时移动滑块变得跳跃

Posted

技术标签:

【中文标题】在 iOS 中播放 Avplayer 时移动滑块变得跳跃【英文标题】:Moving slider getting jumpy while playing Avplayer in iOS 【发布时间】:2020-07-07 12:02:33 【问题描述】:

我在 Avplayer 中播放 mp3 网址。它工作正常,我正在显示 Slider 以了解音频播放器的进度。在尝试向前移动滑块时,它变得神经兮兮的。但音频搜索工作正常。只有滑块 UI 变得跳跃。

我的代码是

  /* ----------------------------------------------------
         Player setup
         ------------------------------------------------------ */
        func playerSetup(url:String) 
            do 
                let audioURL = url
                avPlayerItem = AVPlayerItem( url:NSURL( string:audioURL )! as URL )
                avPlayer = AVPlayer(playerItem:avPlayerItem)
                avPlayer?.rate = 1.0
                avPlayer?.play()
                self.sliderSetup()
                self.timeObserverSetup()
            
        

/* ----------------------------------------------------
     Slider setup
     ------------------------------------------------------ */
    func sliderSetup() 
        DispatchQueue.global(qos: .background).async 
            let duration : CMTime = self.avPlayerItem!.asset.duration
            let seconds : Float64 = CMTimeGetSeconds(duration)
            DispatchQueue.main.async 
                self.slider!.maximumValue = Float(seconds)
            
        
        slider.isContinuous = false
        slider!.minimumValue = 0
        slider!.value = 0
        slider.addTarget(self, action: #selector(MyViewController.playbackSliderValueChanged(_:event:)), for: .valueChanged)
    
    /* ----------------------------------------------------
     Avplayer Observer timer setup
     ------------------------------------------------------ */
    func timeObserverSetup() 
        let interval = CMTime(seconds: 0.05, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        timeObserver = avPlayer!.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using:  [weak self] elapsedTime in
            //  print("observer running")
            self!.updateSlider(elapsedTime: elapsedTime)
        )
    
    
    /* ----------------------------------------------------
     update slider with avplayer duration
     ------------------------------------------------------ */
    func updateSlider(elapsedTime: CMTime) 
        let playerDuration = playerItemDuration()
        if CMTIME_IS_INVALID(playerDuration) 
            slider.minimumValue = 0.0
            return
        
        let duration = Float(CMTimeGetSeconds(playerDuration))
        if duration.isFinite && duration > 0 
            slider.minimumValue = 0.0
            slider.maximumValue = duration
            let time = Float(CMTimeGetSeconds(elapsedTime))
            slider.setValue(time, animated: true)
        
    
    
    /* ----------------------------------------------------
     avplayeritem duration
     ------------------------------------------------------ */
    private func playerItemDuration() -> CMTime 
        let thePlayerItem = avPlayer?.currentItem
        if thePlayerItem?.status == .readyToPlay 
            return thePlayerItem!.duration
        
        return CMTime.invalid
    
    
    /* ----------------------------------------------------
     Slider Action
     ------------------------------------------------------ */
    @objc func playbackSliderValueChanged(_ playbackSlider:UISlider, event: UIEvent) 
        DispatchQueue.main.async 

            let seconds : Int64 = Int64(self.slider.value)
            let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
            self.avPlayer!.seek(to: targetTime)
            
            if let touchEvent = event.allTouches?.first 
                switch touchEvent.phase 
                case .began:
                    self.removeTimerObserver()
                    self.pauseAudio()
                    break
                    
                case .moved:
                    break
                    
                case .ended:
                    self.timeObserverSetup()
                    self.playAudio()
                    break
                    
                default:
                    break
                
            
        
    
    
    /* ----------------------------------------------------
     Remove timer observer
     ------------------------------------------------------ */
    func removeTimerObserver() 
        if ((self.timeObserver) != nil) 
            if self.avPlayer?.rate == 1.0  // it is required as you have to check if player is playing
                self.avPlayer?.removeTimeObserver(self.timeObserver as Any)
                self.timeObserver = nil
            
        
    

我看过其他论坛,他们建议在开始滑块触摸时,我们可以删除时间观察者,然后滑块触摸结束我们可以添加观察者,所以,我遵循了。

但是,问题仍然存在。

有什么建议吗?

【问题讨论】:

【参考方案1】:

我已经解决了这个问题。

问题是由于滑块的以下属性而发生的。

    slider.isContinuous = false

所以,这个我已经删除了,现在可以正常工作了。

我还在 DispatchQueue.main.async 中运行滑块操作方法。所以,我把它改成了跟随。

/* ----------------------------------------------------
 Slider Action
 ------------------------------------------------------ */
@objc func playbackSliderValueChanged(_ playbackSlider:UISlider, event: UIEvent) 

        let seconds : Int64 = Int64(self.slider.value)
        let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
        self.avPlayer!.seek(to: targetTime)
        
        if let touchEvent = event.allTouches?.first 
            switch touchEvent.phase 
            case .began:
                self.removeTimerObserver()
                self.pauseAudio()
                break
                
            case .moved:
                break
                
            case .ended:
                self.timeObserverSetup()
                self.playAudio()
                break
                
            default:
                break
            
        

现在一切正常。

【讨论】:

以上是关于在 iOS 中播放 Avplayer 时移动滑块变得跳跃的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 使用 AVPlayer 在锁定屏幕上倒带歌曲

UISlider 不能与 AVPlayer 一起正常工作

AVPlayer 进度滑块随用户拖动而变化

ViewController 加载时 UISlider 的 AVPlayer currentTime 更新

使WordPress垂直导航的滑块变大

获取AVPlayer ios中视频播放的当前时间