8

I am making an app that has a music player in it. I haven't worked on it in a while but the last time I worked on it the nowPlayingInfo was working. Now, when I updated it to Swift 3, everything works except for the progress bar.

I have seen a few similar questions on here that talked about race conditions and it being overridden by something but I can't seem to figure out by what. I am using an AVQueuePlayer if that helps.

The skip and previous buttons work, the album art, title, and artist update perfectly but the elapsed time just isn't updating. When I debug, the MPNowPlayingInfoPropertyElapsedPlaybackTime from MPNowPlayingInfoCenter.default().nowPlayingInfo is showing the correct numbers, but the actual screen in control center is not. Usually, the progress is stuck at 0:01 and just does not move. However, if I seek using the slider in my app and go back to control center, it shows the time I seeked to but stays at that.

Here is my setNowPlaying code:

func setNowPlaying(_ dura: Float, timePlayed: Float) {
    let s = songs[playbackInstance.currentSongIndex]
    let albumArt = MPMediaItemArtwork.init(boundsSize: CGSize(width: 480, height: 360), requestHandler: { (size) -> UIImage in
        return self.songImageView.image ?? #imageLiteral(resourceName: "WhiteMusic")
    })
    let songInfo: [String: Any]? = [
        MPMediaItemPropertyTitle: s.name,
        MPMediaItemPropertyArtist: s.artist,
        MPMediaItemPropertyArtwork: albumArt,
        MPMediaItemPropertyPlaybackDuration: dura,
        MPNowPlayingInfoPropertyElapsedPlaybackTime: CMTimeGetSeconds(player.currentTime())
    ]

    MPNowPlayingInfoCenter.default().nowPlayingInfo = songInfo
}

I used to set MPNowPlayingInfoPropertyElapsedPlaybackTime to the timePlayed variable that is passed in to the method, but since it wasn't working I tried player.currentTime() as recommended by other questions and it is probably a better measure than what I was using anyways.

Here is the code for seeking in case it helps:

@IBAction func sliderChanged(_ sender: UISlider) {
    if timer.isValid {
        currentTimeLabel.text = secondsToText(sender.value)
        player.seek(to: CMTimeMakeWithSeconds(Float64(sender.value), player.currentItem!.currentTime().timescale))
    }
}

For some reason that is the only thing that will update the control center info.

lagoon
  • 6,417
  • 6
  • 23
  • 30

2 Answers2

0

This has worked for me, maybe it will help you.

         func setNowPlaying(_ dura: Float, timePlayed: Float) {
            let s = songs[playbackInstance.currentSongIndex]
            let albumArt = MPMediaItemArtwork.init(boundsSize: CGSize(width: 480, height: 360), requestHandler: { (size) -> UIImage in
                return self.songImageView.image ?? #imageLiteral(resourceName: "WhiteMusic")
            })

            MPNowPlayingInfoCenter.default().nowPlayingInfo = [
                MPMediaItemPropertyTitle: s.name,
                MPMediaItemPropertyArtist: s.artist,
                MPMediaItemPropertyArtwork: albumArt as Any,
                MPMediaItemPropertyPlaybackDuration: dura,
                MPNowPlayingInfoPropertyElapsedPlaybackTime : player.currentTime()
            ]
            UIApplication.shared.beginReceivingRemoteControlEvents()
            becomeFirstResponder()

        }


        @IBAction func sliderAction(_ sender: Any) {
                if player != nil {
                    Thread.cancelPreviousPerformRequests(withTarget: self)
                    self.perform(#selector(playAtSelectedTime) , with: nil, afterDelay: 0.2)
                }
            }

        @objc func playAtSelectedTime(){
                let selectedTime = Double(progressSlider.value)
                player.currentTime = selectedTime
                convertTimingToTextLabel(selectedTime, label: self.runningLabel)
                convertTimingToTextLabel(Double(player.duration-player.currentTime), label: self.durationLabel)
                durationLabel.text = " -"+durationLabel.text!
                updateSlider()
            }   


        func updateSlider(){
                if timer != nil{
                    timer.invalidate()
                }

                timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(playingSong) , userInfo: nil, repeats: true)
            }


        @objc func playingSong(){
                progressSlider.minimumValue = 0
                progressSlider.maximumValue = Float(player.duration)
                progressSlider.value = Float(player.currentTime)

                convertTimingToTextLabel(Double(player.currentTime), label: self.runningLabel)
                convertTimingToTextLabel(Double(player.duration-player.currentTime), label: self.durationLabel)
                durationLabel.text = durationLabel.text!
            }



            func convertTimingToTextLabel(_ time: Double, label: UILabel) {
                    let minute = Int(time / 60)
                    let seconds = Int(time - Double(minute * 60))
                    setTimingSongForLabel(minute, seconds: seconds, label: label)
                }

            func setTimingSongForLabel(_ minute: Int, seconds: Int, label: UILabel) {
                    let mStr = minute > 9 ? "\(minute)":"0\(minute)"
                    let sStr = seconds > 9 ? "\(seconds)":"0\(seconds)"
                    label.text = "\(mStr):\(sStr)"
                }
cwilliamsz
  • 718
  • 9
  • 20
  • If you have any questions or still doesn't work, please post the full code of your player so you can help you. – cwilliamsz Feb 10 '18 at 05:55
  • I don't understand what the code is doing, or supposed to do, or any part of it. –  Sep 15 '20 at 23:16
0

func setNowPlaying(_ dura: Float, timePlayed: Float) should be called by a Timer.

func setNowPlaying(_ dura: Float, timePlayed: Float) is just a simple method, it won't updates if you don't call it.

You should use a Timer to call it regularly, to update the current play time.

Often times, the player goes, the progress bar follows. you can call func setNowPlaying(_ dura: Float, timePlayed: Float) in the same timer method

dengST30
  • 3,643
  • 24
  • 25
  • 1
    This is a bad idea. According to Apple docs: Value is an NSNumber object configured as a double. Elapsed time is automatically calculated, by the system, from the previously provided elapsed time and the playback rate. Do not update this property frequently—it is not necessary. https://developer.apple.com/documentation/mediaplayer/mpnowplayinginfopropertyelapsedplaybacktime – Darkisa Aug 29 '21 at 23:44