如何使用单个按钮启动和停止音频?

时间:2017-08-26 15:33:05

标签: ios swift

我正在建立一个睡眠定时器应用程序,允许使用9种不同的声音来帮助你入睡。我需要的最后一件事是能够在点击按钮时播放声音,并在我再次按下时停止相同的声音。我试图这样做:

@IBAction func heavyThunderBtnPressed(_ sender: UIButton) {
    heavyThunderBtn.isSelected = true
    toggleButton(button: sender, onImage: #imageLiteral(resourceName: "thunderstorm_selected"), offImage: #imageLiteral(resourceName: "thunderstorm_unselected"))
    numberOfPresses += 1


    if numberOfPresses % 2 == 0 || numberOfPresses == 1 && numberOfPresses != 2 {
    do {
        rainAudioPlayer = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "thunder", ofType: "mp3")!))
        rainAudioPlayer.prepareToPlay()
        rainAudioPlayer.play()
    }
    catch {
        print(error)
    }
}
    else {
        rainAudioPlayer.stop()

    }
}

但它允许我按下按钮,它会播放音频,按钮会变为我选择的颜色,表明它已经开启。然后当我再次按下它时,音频重新开始并继续播放,然后当我再次按下它时,它会停止,但按钮再次变为蓝色,表示它已打开。所以这就是我改变它的原因,但每次按下按钮播放音频时我都会崩溃。我得到的错误是" EXC_BAD_ACCESS(代码= 1,地址= 0 x 48)"

import UIKit
import AVFoundation

class RainSoundsViewController: UIViewController {

var rainAudioPlayer = AVAudioPlayer()
var numberOfPresses = 0
var tappedAgain: Bool! = false

var heavyThunderOffImage = UIImage()

@IBOutlet weak var backgroundView: UIView!

@IBOutlet weak var heavyThunderBtn: UIButton!

@IBOutlet weak var lightRainBtn: UIButton!

@IBOutlet weak var rainOnRoofBtn: UIButton!


override func viewDidLoad() {
    super.viewDidLoad()

    heavyThunderOffImage = #imageLiteral(resourceName: "thunderstorm_unselected")

    heavyThunderBtn.isSelected = false

    backgroundView.layer.cornerRadius = 10
    backgroundView.layer.masksToBounds = true


}

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

@IBAction func heavyThunderBtnPressed(_ sender: UIButton) {
    heavyThunderBtn.isSelected = true
    toggleButton(button: sender, onImage: #imageLiteral(resourceName: "thunderstorm_selected"), offImage: #imageLiteral(resourceName: "thunderstorm_unselected"))
    numberOfPresses += 1


    if rainAudioPlayer.isPlaying {

    do {
        rainAudioPlayer = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "thunder", ofType: "mp3")!))
        rainAudioPlayer.prepareToPlay()
        rainAudioPlayer.play()
    }
    catch {
        print(error)
    }
}
    else {
        rainAudioPlayer.stop()

    }
}

2 个答案:

答案 0 :(得分:1)

代码中最重要的缺陷存在于此行中:

var rainAudioPlayer = AVAudioPlayer()

初始化程序init()没有为AVAudioPlayer定义良好,结果不可预测。实际上,这个初始化程序会创建一个完全无用的对象。

并且rainAudioPlayer在上面的声明中变为非可选项,因此您无法检查它是否已正确初始化。

将其设为可选。不要给它一个无用的初始值。

var rainAudioPlayer: AVAudioPlayer?

通过此更改,您的代码的其他一些部分需要修复。例如,if - 语句的条件是:

if rainAudioPlayer?.isPlaying ?? false {

或者:

if let player = rainAudioPlayer, player.isPlaying {

完整代码以及其他一些建议:

import UIKit
import AVFoundation

class RainSoundsViewController: UIViewController {

    var rainAudioPlayer: AVAudioPlayer? //### You should not instantiate a useless instance.
    var numberOfPresses = 0
    var tappedAgain: Bool = false //<- You have no reason to make this Implicitly Unwrapped Optional.

    var heavyThunderOffImage: UIImage! //<- Do you really need this?

    @IBOutlet weak var backgroundView: UIView!

    @IBOutlet weak var heavyThunderBtn: UIButton!

    @IBOutlet weak var lightRainBtn: UIButton!

    @IBOutlet weak var rainOnRoofBtn: UIButton!


    override func viewDidLoad() {
        super.viewDidLoad()

        heavyThunderOffImage = #imageLiteral(resourceName: "thunderstorm_selected")

        heavyThunderBtn.isSelected = false

        backgroundView.layer.cornerRadius = 10
        backgroundView.layer.masksToBounds = true
    }

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

    @IBAction func heavyThunderBtnPressed(_ sender: UIButton) {
        heavyThunderBtn.isSelected = true
        toggleButton(button: sender, onImage: #imageLiteral(resourceName: "thunderstorm_selected"), offImage: #imageLiteral(resourceName: "thunderstorm_unselected"))
        numberOfPresses += 1

        if rainAudioPlayer?.isPlaying ?? false { //### `rainAudioPlayer` is now Optional.

            do {
                rainAudioPlayer = try AVAudioPlayer(contentsOf: Bundle.main.url(forResource: "thunder", withExtension: "mp3")!) //<- You can use `url(forResource:withExtension:)`.
                //`play()` internally calls `prepareToPlay()`, you have no need to call it here.
                rainAudioPlayer?.play()
            }
            catch {
                print(error)
            }
        }
        else {
            rainAudioPlayer?.stop() //<- Here, `rainAudioPlayer` is NOT playing, do you need to stop it?
        }
    }

    //...
}

我没有检查此代码的其他部分是否按预期工作,但不会再导致 EXC_BAD_ACCESS

答案 1 :(得分:0)

不确定您使用numberOfPresses逻辑做了什么。似乎超级混乱。

以这种方式简化你的逻辑。 (注意:这是在Objective-C中)

static boolean soundPlaying=false; //declare a static variable

//now in your button you turn on/off the sound this way
- (IBAction) heavyThunderBtnPressed
{
   if (soundPlaying == false)
   {
     //initialize your AVAudioPlayer play sound. add code here
     // ....
     soundPlaying = true; //set this to true as you are playing the sound
   }
   else
   {
     //sound was already playing so stop it. add code here
     // ....
     soundPlaying = false; //set this to false as you have stopped the sound
   }

}