Search code examples
iosswift3avaudioplayer

Swift 3: Background music plays again every time the view controller loads


My background music plays once the view controller has loaded. which is great, however every time the user returns to that screen it plays again. and layers itself into a incoherent mess. I need the music to play once and continue to play until the user turns it off in the settings.

As I understand from the code i've written. Once the view loads in creates the audioPlayer it then checks to see if the boolean isPlaying is set to false. If it is then it executes the playMusic function. if its set to true it should do nothing. I am confused on why it plays the music again on top of the previous iteration.

Could it be creating the audio player every time the view is loaded? if so how would i go about fixing that?

Once the app is loaded the user is presented with a start screen. the music starts playing there.

They then press either start, purchase hints, or settings. in order to go back to the previous screen you hit a back button. which the way the design of the app is setup, always takes you back to the start screen. the segues are just set up in that. i control dragged to the next view controller and chose the show option.

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var audioPlayer = AVAudioPlayer()

    @IBOutlet weak var  musicToggle: UIButton!

    var isPlaying = false

    @IBAction func musicTogglebtn(_ sender: Any) {

    }

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            audioPlayer = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "weiss", ofType: "mp3")!))
            audioPlayer.prepareToPlay()
        } catch {
            print(error)
        }

        if isPlaying == false {
            playMusic()
        } else {
            return
        }
    }

    func playMusic() {
        audioPlayer.play()
        isPlaying = true
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

Solution

  • The key piece of information you don't provide is how does the user get to and from this screen?

    If you present/push another view controller on top of it and the user dismisses/pops that view controller, your view controller with the audio player will be revealed, with it's state still intact.

    If you pop/dismiss your ViewController and then present a new one, or keep pushing/presenting a new copy on top of the previous one then each view controller will have it's own state, and its own audio player so you'll get more and more sounds playing on top of each other.

    My guess is that is your problem.

    Post information about how the user navigates your view controllers and we can help you sort it out.

    EDIT:

    Based on your replies, I understand your problem now.

    The sound playing issue is a symptom of a bigger problem. You have set up your back button to trigger a show segue. That creates an additional, brand-new copy of your view controller which gets added to the top of the navigation stack. That is wrong, and will lead to lots and lots of problems.

    You should set up your navigation controller to show a navigation bar. When you do that, it will set up a back button for you, and that back button will pop the front view controller off the stack and expose the one underneath. That is the expected behavior of a back button.

    If instead you want your back button to take you all the way to the start screen then you should remove the segue link from the back button and instead connect it to an IBAction that sends the message popToRootViewController to the navigation controller.

    This is going to lead to your next problem, which is when you click the sound back button, you're going to close the current view controller and lose access to your audio player.

    You should probably move your sound playing control to a central sound manager object. This would be a good use for a singleton. (Look that up.) Add methods to your sound manager singleton that let you start and stop sounds, and invoke the sound play method when you display your view controller, and stop the sound from playing when you want it to stop.