3

I am creating a simple music app, and I was wondering how I can make a UiSlider to follow the progress of a audio file. Here's my project so far:

Storyboard

Code:

import UIKit
import AVFoundation

class SongDetailViewController: UITableViewController {

    var audioPlayer = AVAudioPlayer()

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Song Name", ofType: "mp3")!))
            audioPlayer.prepareToPlay()

            var audioSession = AVAudioSession.sharedInstance()

            do {
                try audioSession.setCategory(AVAudioSessionCategoryPlayback)
            }
        }

        catch {
            print(error)
        }
    }
//    Buttons

//    Dismiss
    @IBAction func dismiss(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }

//    Play
    @IBAction func play(_ sender: Any) {
        audioPlayer.stop()
        audioPlayer.play()
    }

//    Pause
    @IBAction func pause(_ sender: Any) {
        audioPlayer.pause()
    }

//    Restart
    @IBAction func restart(_ sender: Any) {
        audioPlayer.currentTime = 0
    }

}

I'm wanting to create the uislider similar to the Apple Music app where it follows the audio file's progress and whenever the user slides the ball thing (lol) it goes to that time of the song. If you could give me some code to complete this, that would be amazing!

Please keep in mind that I am fairly new to coding and am still learning swift, so keep it simple :-) Thanks again!

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
iFunnyVlogger
  • 437
  • 1
  • 6
  • 17

2 Answers2

2

If you switch to using an AVPlayer, you can add a periodicTimeObserver to your AVPlayer. In the example below you'll get a callback every 1/30 second…

let player = AVPlayer(url: Bundle.main.url(forResource: "Song Name", withExtension: "mp3")!)

player.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 30), queue: .main) { time in

    let fraction = CMTimeGetSeconds(time) / CMTimeGetSeconds(player.currentItem!.duration)

    self.slider.value = fraction
}

Where you create an audioPlayer in your code, replace with the code above.

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • Ok, so, can you take my code above and put your code into it where it is 100% completed? If you need to put a IB outlet for the slider, put that in it too. Is that ok? Thanks A LOT!! – iFunnyVlogger Mar 07 '17 at 15:46
  • 1
    Do you think you could attempt it yourself, and update your question if you're still having problems? – Ashley Mills Mar 07 '17 at 15:47
  • Updated my answer. It's bad form to expect people on SO to do the work for you. You need to try things for yourself and then ask when you have problems. – Ashley Mills Mar 07 '17 at 15:53
  • 1
    "...can you take my code above and put your code into it where it is 100% completed?" You're kidding, right? "Can you do all my work for me?" "My car needs washing, do you mind?" This is not a site where other people do you work for you. Put in some effort and we'll help you, but don't ask to have solutions handed toy "100% completed". – Duncan C Mar 07 '17 at 16:17
0

Using AVAudioPlayer you could create a periodic timer that fires several times a second (up to 60 times/second - any more would be a waste) and updates your slider based on your audio player's currentTime property. To sync the update with screen refresh you could use a CADisplayLink timer.


Edit:

This part of my answer doesn't work:

It should also be possible to set up a Key Value Observer on your AVAudioPlayers currentTime property so that each time the value changes your observer fires. (I haven't tried this, but it should work.)


Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Pretty sure `currentTime` does not support KVO, unless things changed. Their docs said KVO not suitable for continuous state changes. https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/observing_the_playback_time – samwize Mar 26 '20 at 07:28