UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: How to update UISlider in real time so it can be in sync with a song that's playing.

Forums > Swift

HI!
I am trying to create a basic music app and I am using AVAudioPlayer to play the songs. I have a slider that I am using to seek through the song that's playing, and it's working in that fashion. However, what I am having trouble with is getting the slider to update in real time along with the song that's playing. So when a song starts to play, the slider should start updating as well and continue to do so until the song ends. Currently, the slider doesn't even move at all unless I drag, in which case it will work and update as you'd expect. I set the slider's maximum value, and I also set it to be continous.
I use a timer inside this method, to get the slider to update in real time and reflect song time continuously.

    @objc func seekSliderChanged(_ slider: UISlider) {

        let duration = player!.duration
        slider.maximumValue = Float(duration)
        let time = duration/duration
        //seeking through current gabay
        player!.currentTime = TimeInterval(slider.value)

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: {[weak self]
            _ in
            if let weakSelf = self {

                    slider.value = Float(weakSelf.player!.currentTime)

            }

        })

    }
here's the slider properties being set.

```

     //seek through sider
  seekSlider = UISlider()
  seekSlider.isContinuous = true
  seekSlider.maximumValue = Float(player!.duration)
  seekSlider.isUserInteractionEnabled = true
  seekSlider.translatesAutoresizingMaskIntoConstraints = false
  seekSlider.thumbTintColor = UIColor.clear
  //      seekSlider.setThumbImage(UIImage(named: "thumb"), for: .normal)
  seekSlider.addTarget(self, action: #selector(seekSliderChanged), for: .valueChanged)

  holder.addSubview(seekSlider)

```

and here's the whole code

```

       //
      / /  PLayerViewController.swift
     //  Maanso
     //

    //

    import UIKit
    import AVFoundation

      class PLayerViewController: UIViewController, AVAudioPlayerDelegate {
      //seek through sider
     var seekSlider: UISlider!

 @IBOutlet var holder: UIView!
var Git = UILabel()
var gabayArray = [Gabays]()
var currentIndex = 0
var position: Int = 0

var player: AVAudioPlayer?

//user interface elements
var coverImageView: UIImageView!
var gabayName: UILabel!
var gabyaaName: UILabel!

//buttons
var playPauseButton: UIButton!
var backButton: UIButton!
var forwardButton: UIButton!

var timer: Timer!

override func viewDidLoad() {
    super.viewDidLoad()
    configure()
    player!.delegate = self

  //        configureUI()
}

override func viewWillAppear(_ animated: Bool) {
    if let player = player {
 //            player.play()
        player.prepareToPlay()
    }
}

override func viewDidLayoutSubviews() {
    if holder.subviews.count == 0 {
        configure()
    }
}

func configure() {
    //set up player
    let gabay = gabayArray[position]
    let urlString = Bundle.main.path(forResource: gabay.gabayName, ofType: "mp3")

    do {
        try AVAudioSession.sharedInstance().setMode(.default)
        try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)

        guard let urlString = urlString else{return}

        player = try AVAudioPlayer(contentsOf: URL(string: urlString)!)
        guard let player = player else{return}

        player.volume = 0.5
        player.delegate = self

        player.play()

    }

    catch{
        print("error is: \(error.localizedDescription)")
    }
   configureUI()

}

//MARK: - actions
@objc func volumeSliderChanged(_ slider: UISlider) {
    let value = slider.value
    //adjust player volume
    guard let player = player else{return}
    player.volume = value

}

@objc func playPauseButtonTapped(_ slider: UISlider) {

    guard let player = player else{return}

    if player.isPlaying {
        player.stop()
        playPauseButton.setBackgroundImage(UIImage(systemName: "play.fill"), for: .normal)

    }else{
        player.play()
        playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)

    }

}

    @objc func backButtonTapped() {
    if position > 0
    {
        position = position - 1
        player!.stop()
        for subview in holder.subviews {
            subview.removeFromSuperview()
        }
        configure()

// configureUI() } }

@objc func forwardButtonTapped() {
    if position < gabayArray.count - 1{
        position = position + 1
        player!.stop()

        for subview in holder.subviews {
            subview.removeFromSuperview()
        }
        configure()

// configureUI() } }

@objc func seekSliderChanged(_ slider: UISlider) {

    let duration = player!.duration
    slider.maximumValue = Float(duration)
    let time = duration/duration
    //seeking through current gabay
    player!.currentTime = TimeInterval(slider.value)

    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: {[weak self]
        _ in
        if let weakSelf = self {

                slider.value = Float(weakSelf.player!.currentTime)

        }

    })

}

public func configureUI() {

    //set up user interface
    let gabay = gabayArray[position]

    //set up imagecoverview
    coverImageView = UIImageView()
    coverImageView.contentMode = .scaleAspectFill
    coverImageView.image = UIImage(named: gabay.imageName)
    coverImageView.translatesAutoresizingMaskIntoConstraints = false
    holder.addSubview(coverImageView)

    //set up the two labels
    gabayName = UILabel()
    gabayName.textAlignment = .center
    gabayName.numberOfLines = 0
    gabayName.translatesAutoresizingMaskIntoConstraints = false
    gabayName.text = gabay.gabayName
    gabayName.font = UIFont(name: "helvetica", size: 20)
    holder.addSubview(gabayName)

    gabyaaName = UILabel()
    gabyaaName.translatesAutoresizingMaskIntoConstraints = false
    gabyaaName.text = gabay.gabyaaName
    gabyaaName.font = UIFont(name: "helvetica", size: 18)
    gabyaaName.textAlignment = .center
    gabyaaName.numberOfLines = 0
    holder.addSubview(gabyaaName)

    //set up sliders.
    let volumeSlider = UISlider()
    volumeSlider.value = 0.5
    volumeSlider.translatesAutoresizingMaskIntoConstraints = false
    volumeSlider.addTarget(self, action: #selector(volumeSliderChanged), for: .valueChanged)
    volumeSlider.layer.cornerRadius = 8
    volumeSlider.isUserInteractionEnabled = true
    holder.addSubview(volumeSlider)

  //seek through sider
  seekSlider = UISlider()
  seekSlider.isContinuous = true
  seekSlider.maximumValue = Float(player!.duration)
  seekSlider.isUserInteractionEnabled = true
  seekSlider.translatesAutoresizingMaskIntoConstraints = false
  seekSlider.thumbTintColor = UIColor.clear

// seekSlider.setThumbImage(UIImage(named: "thumb"), for: .normal) seekSlider.addTarget(self, action: #selector(seekSliderChanged), for: .valueChanged)

  holder.addSubview(seekSlider)

    //set up buttons, play button
    playPauseButton = UIButton()
    playPauseButton.tintColor = .black
    playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
    playPauseButton.translatesAutoresizingMaskIntoConstraints = false
    playPauseButton.addTarget(self, action: #selector(playPauseButtonTapped), for: .touchUpInside)
    holder.addSubview(playPauseButton)

    //back button
    backButton = UIButton()
    backButton.tintColor = .black
    backButton.setBackgroundImage(UIImage(systemName: "backward.fill"), for: .normal)
    backButton.translatesAutoresizingMaskIntoConstraints = false
    backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
    holder.addSubview(backButton)

    //forward button
     forwardButton = UIButton()

    forwardButton.tintColor = .black
    forwardButton.setBackgroundImage(UIImage(systemName: "forward.fill"), for: .normal)
    forwardButton.translatesAutoresizingMaskIntoConstraints = false
    forwardButton.addTarget(self, action: #selector(forwardButtonTapped), for: .touchUpInside)
    holder.addSubview(forwardButton)

  //volume buttons

  let rightVolButton = UIButton()
  rightVolButton.translatesAutoresizingMaskIntoConstraints = false
  rightVolButton.setBackgroundImage(UIImage(systemName: "volume.3.fill"), for: .normal)
  holder.addSubview(rightVolButton)

    //set up constraints
    NSLayoutConstraint.activate([
        coverImageView.topAnchor.constraint(equalTo: holder.layoutMarginsGuide.topAnchor, constant: 80),
        coverImageView.leadingAnchor.constraint(equalTo: holder.layoutMarginsGuide.leadingAnchor, constant: 0),
        coverImageView.trailingAnchor.constraint(equalTo: holder.layoutMarginsGuide.trailingAnchor, constant: 0),

        gabayName.topAnchor.constraint(equalTo: coverImageView.bottomAnchor, constant: 100),
        gabayName.centerXAnchor.constraint(equalTo: holder.centerXAnchor),

        gabyaaName.topAnchor.constraint(equalTo: gabayName.bottomAnchor, constant: 15),
        gabyaaName.centerXAnchor.constraint(equalTo: holder.centerXAnchor),

        //seek slider
        seekSlider.topAnchor.constraint(equalTo: gabyaaName.topAnchor, constant: 30),
        seekSlider.centerXAnchor.constraint(equalTo: holder.centerXAnchor),
        seekSlider.widthAnchor.constraint(equalTo: holder.widthAnchor, multiplier: 0.8),

        //buttons
        playPauseButton.topAnchor.constraint(equalTo: seekSlider.bottomAnchor, constant: 25),
        playPauseButton.centerXAnchor.constraint(equalTo: holder.centerXAnchor),
        playPauseButton.widthAnchor.constraint(equalToConstant: 70),
        playPauseButton.heightAnchor.constraint(equalToConstant: 70),

        backButton.topAnchor.constraint(equalTo: seekSlider.bottomAnchor, constant: 25),
        backButton.leadingAnchor.constraint(equalTo: holder.leadingAnchor, constant: 20),
        backButton.widthAnchor.constraint(equalToConstant: 70),
        backButton.heightAnchor.constraint(equalToConstant: 70),

        forwardButton.topAnchor.constraint(equalTo: seekSlider.bottomAnchor, constant: 25),
        forwardButton.trailingAnchor.constraint(equalTo: holder.trailingAnchor, constant: -20),
        forwardButton.widthAnchor.constraint(equalToConstant: 70),
        forwardButton.heightAnchor.constraint(equalToConstant: 70),

        //volume slider
        volumeSlider.topAnchor.constraint(equalTo: playPauseButton.bottomAnchor, constant: 30),
        volumeSlider.centerXAnchor.constraint(equalTo: holder.centerXAnchor),
        volumeSlider.widthAnchor.constraint(equalTo: holder.widthAnchor, multiplier: 0.8),

        rightVolButton.topAnchor.constraint(equalTo: playPauseButton.bottomAnchor, constant: 35),
        rightVolButton.trailingAnchor.constraint(equalTo: holder.layoutMarginsGuide.trailingAnchor, constant: 0),

    ])

}

//MARK: - AVAudioplayer delegate func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { if flag, position < gabayArray.count - 1 { position = position + 1

        for subview in holder.subviews{
            subview.removeFromSuperview()
        }
        configure()

    }
    else{
        position = 0
        for subview in holder.subviews{
            subview.removeFromSuperview()
        }
        configure()
    }

    }

override func viewWillDisappear(_ animated: Bool) {
    player?.stop()
}

    }

```

2      

@Shah_222 I was stuck on this for day!

Thank you so much for providing the solution. It works, however, the slider is bit behind the song, the songs ends before slider ends. They are not in sync. If you have more time, help me please!

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.