Search code examples
swiftuibuttonavaudioplayerviewcontroller

Swift: multiple functions working at the same time


In my app I have 2 UIButtons, each button plays 2 sounds (plays one sound, pauses, plays the other sound), and the UIButtons image changes every time it is pressed, my app also has a few tabs.the problem is that when I press a UIButton (which takes about 4 seconds for the sounds to be played and be done) I can not press another button, or even switch between tabs, the app freezes until the action of UIbutton gets done with, then I can press other buttons or switch tabs. here's my code:

import UIKit
import AVFoundation
import CoreData


class FirstViewController: UIViewController {

var player:AVAudioPlayer = AVAudioPlayer()

var player2:AVAudioPlayer = AVAudioPlayer()


@IBAction func play(_ sender: Any) {

    player.play()
    sleep(1)
    player2.play()
}

@IBAction func play2(_ sender: Any) {
    player.play()
    sleep(1)
    player2.play()  
}


@IBOutlet weak var play: UIButton!
@IBOutlet weak var play2: UIButton!

var buttonActive = false

@IBAction func pPressed(_ sender: Any) {
    if buttonActive {
        play.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
    } else {
        play.setBackgroundImage(UIImage(named: "Switch-Icon-40"), for: .normal)
    }
    buttonActive = !buttonActive
}

@IBAction func p2Pressed(_ sender: Any) {
    if buttonActive {
        play2.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
    } else {
        play2.setBackgroundImage(UIImage(named: "Switch-Icon-40"), for: .normal)
    }
    buttonActive = !buttonActive
}

override func viewDidLoad() {
    super.viewDidLoad()
    play.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
    play2.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)

do

    {

        let audioPath = Bundle.main.path(forResource: "DTMF-1", ofType: "mp3")
        try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)

        let audioPath2 = Bundle.main.path(forResource: "DTMF-2", ofType: "mp3")
        try player2 = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath2!) as URL)

 }

Is there any way to make my app be faster?


Solution

  • One famous way to implement do something, pause and do other thing is to use Timer.

    The following code also includes:

    • How to properly instantiate AVAudioPlayer
    • How to invalidate working Timer
    • How to stop AVAudioPlayer when it may be reused soon
    • How to stop playing on transition

    Please try:

    class FirstViewController: UIViewController {
    
        var player: AVAudioPlayer? //### You should not instantiate unused objects
    
        var player2: AVAudioPlayer?
    
        var secondSoundTimer: Timer?
    
        private func startPlaying() {
            stopPlaying()
    
            player?.play()
            //### Using `Timer` is one way to achieve pause-like thing.
            //### If you need to target older iOSs than 10.0, you need to modify this part.
            secondSoundTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) {timer in
                self.player2?.play()
                self.secondSoundTimer?.invalidate()
                self.secondSoundTimer = nil
            }
        }
    
        private func stopPlaying() {
            secondSoundTimer?.invalidate()
            secondSoundTimer = nil
            if let player = player, player.isPlaying {
                //### Stop playing (keeping internal `prepared` state) and rewind to the first place.
                player.pause()
                player.currentTime = 0
            }
            if let player2 = player2, player2.isPlaying {
                player2.pause()
                player2.currentTime = 0
            }
        }
    
        @IBOutlet weak var play: UIButton!
        @IBOutlet weak var play2: UIButton!
    
        var buttonActive = false
    
        @IBAction func pPressed(_ sender: Any) {
            startPlaying()
            if buttonActive {
                play.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
            } else {
                play.setBackgroundImage(UIImage(named: "Switch-Icon-40"), for: .normal)
            }
            buttonActive = !buttonActive
        }
    
        //### Don't you need `button2Active`?
    
        @IBAction func p2Pressed(_ sender: Any) {
            startPlaying()
            if buttonActive {
                play2.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
            } else {
                play2.setBackgroundImage(UIImage(named: "Switch-Icon-40"), for: .normal)
            }
            buttonActive = !buttonActive
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            play.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
            play2.setBackgroundImage(UIImage(named: "SwitchD-Icon-40"), for: .normal)
    
            do {
                //### You can get URL directly from Bundle
                //### It's better to apply forced-unwrapping as soon as possible, if you need it eventually.
                let audioURL = Bundle.main.url(forResource: "DTMF-1", withExtension: "mp3")!
                player = try AVAudioPlayer(contentsOf: audioURL)
                //### `prepareToPlay()` improves response for the first play.
                player?.prepareToPlay()
    
                let audioURL2 = Bundle.main.url(forResource: "DTMF-2", withExtension: "mp3")!
                player2 = try AVAudioPlayer(contentsOf: audioURL2)
                player2?.prepareToPlay()
            } catch {
                print(error)
                //... or any other things you need...
            }
    
            //...
        }
    
        //### Stop playing on transition to other ViewConrollers.
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
    
            stopPlaying()
        }
    }