【问题标题】:How to run a function in a for loop sequentially如何在for循环中按顺序运行函数
【发布时间】:2020-03-18 04:36:54
【问题描述】:

我在堆栈视图中有一组按钮。按下每个按钮都会播放不同的声音。我有一个单独的按钮(循环按钮),按下时会调用loopButtonPressed 函数。我的目标是,当按下此循环按钮时,它将循环通过作为此堆栈视图中按钮的子视图,并使用soundButtonPressed 函数按顺序播放每个声音。我看到了我在下面使用run() 函数实现的方法,该函数将每个连续函数设置为在给定时间后运行。虽然这种工作方式它不是一个很好的解决方案,因为声音文件的长度不同。我在想可能有一种方法可以使用调度组来做到这一点,但我并不完全理解。如果我去掉运行功能,它只会播放堆栈视图中最后一个按钮的声音。我也在使用 AVFoundation 来播放 wav 文件。我感谢任何建议或指导,谢谢。

    func run(after seconds: Int, completion: @escaping () -> Void) {
        let deadline = DispatchTime.now() + .milliseconds(seconds)
        DispatchQueue.main.asyncAfter(deadline: deadline) {
            completion()
        }
    }

    @objc func loopButtonPressed(_ sender: UIButton) {
        var i = 1
        for case let button as UIButton in self.colorBubblesStackView.subviews {
            run(after: 800*i) {
                self.soundButtonPressed(sender: button)
            }
            i += 1
        }
    }

我的 soundButtonPressed 函数只是一个 switch 语句,其中每个案例都使用正确的声音文件名调用函数 playSound()。这是 playSound 函数:

func playSound(_ soundFileName: String) {
        guard let url = Bundle.main.url(forResource: soundFileName, withExtension: "wav") else { return }

        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)

            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)

            guard let player = player else { return }

            player.play()

        } catch let error {
            print(error.localizedDescription)
        }
    }

【问题讨论】:

  • 如果你设置了 player.delegate 你可以在声音播放完毕时得到一个回调。这会让您知道何时需要开始播放下一个声音。

标签: ios swift avfoundation synchronous dispatchgroup


【解决方案1】:
func playSound(name: String ) {
        guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else {
            print("url not found")
            return
        }

        do {
            /// this codes for making this app ready to takeover the device audio
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

            /// change fileTypeHint according to the type of your audio file (you can omit this)
            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)

            player?.delegate = self

            // no need for prepareToPlay because prepareToPlay is happen automatically when calling play()
            player!.play()
        } catch let error as NSError {
            print("error: \(error.localizedDescription)")
        }
    }

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("finished")//It is working now! printed "finished"!
    }

确认协议

class ViewController: UIViewController,AVAudioPlayerDelegate {

而不是循环添加每个按钮一个 Tag 并从第一个标签开始 sat 100 。然后,当为播放器获得的回调完成播放时,播放下一个带有新添加标签的文件,例如 101

【讨论】:

    【解决方案2】:

    试试AVQueuePlayer

    用于按顺序播放多个项目的播放器。

    @objc func loopButtonPressed(_ sender: UIButton) {
        let allUrls = allSongs.compactMap { Bundle.main.url(forResource: $0, withExtension: "wav") }
        let items = allUrls.map { AVPlayerItem(url: $0) }
        let queuePlayer = AVQueuePlayer(items: items)
        queuePlayer.play()
    }
    

    【讨论】:

    • 我试过了,但它似乎只播放了第一个声音文件的一小部分。如果我错了,请纠正我,但 allSongs 是应播放的所有歌曲文件名的列表。它在项目下方设置了一个打印语句,并打印出所有正确的 URL,但没有播放所有声音
    • @MaxDolensky 是的。 allSongs 是一组声音名称,如 ["sound1", "sound2"]。您应该在项目中有sound1.wavsound2.wav 文件
    猜你喜欢
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-20
    • 2021-08-31
    • 1970-01-01
    • 2011-01-31
    相关资源
    最近更新 更多