当用户在应用程序中移动时如何继续播放音乐。 [不在后台模式]

How to continuing playing music when user move through the app. [Not in background mode]

我正在 Swift 中制作一个基于 Tab Bar Controller 的 musicPlayer 应用程序,现在正在创建 NowPlayingViewController,它呈现如下屏幕截图所示的播放场景。当我单击另一个 tableViewController 的 table 行并播放该歌曲时,将打开此 NowPlayingViewController。

我的问题是,当我关闭此 View Controller 以返回上一个视图或移动到 Tab Bar Controller 的其他视图时,音乐播放停止了。经过一些谷歌搜索,有人建议使用 singleton 来创建一个共享会话来播放音乐。

/// 这是我项目的故事板

/// 我更改我的代码以添加一个 音频 Manager.swift 作为单例 class。但是我不确定我是否做对了,需要帮助。也没有找到从 NowPlayingVewController 获取 playingSong 变量的方法...

import AVKit

class AudioManager {
    // use Singleton pattern keep the music continuing when user move through the App.
    static let sharedInstance = AudioManager()

    var audioPlayer: AVAudioPlayer!
    var playingSong: SongData?

    private init() {
        // config for audio background playing
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
            print("Playback OK")
            try AVAudioSession.sharedInstance().setActive(true)
            print("Session is Active")
        } catch {
            print(error)
        }
    }

    func startMusic() {
        do {
            // how to retrieve the playingSong variable from NowPlayingViewController?
            // playingSong =
            try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
            audioPlayer.prepareToPlay()
            audioPlayer.play()
        } catch {
            print("could not load file")
        }
    }

    func pauseMusic() {
       audioPlayer.pause()
    }
}

/// NowPlayViewController.swift,以防与上图不完全对齐,因为它只是展示播放场景的示例图像。

import UIKit
import AVKit

class NowPlayingViewController: UIViewController {

    var playingSong: SongData?
    var audioPlayer: AVAudioPlayer!
    var isPlaying = true
    var timer:Timer!

    @IBOutlet weak var songTitle: UILabel!
    @IBOutlet weak var songAlbum: UILabel!
    @IBOutlet weak var songArtwork: UIImageView!
    @IBOutlet weak var playOrPauseButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        songTitle.text = playingSong?.songName
        songAlbum.text = playingSong?.albumName
        songArtwork.image = playingSong?.albumArtwork

        // start play music in AudioManager sharedInstance
        AudioManager.sharedInstance.startMusic()
    }

    @IBAction func NaviBack(_ sender: UIButton) {
        print("press button")
        // not sure about this, use present modally of segue,
        // so need to use dismiss to return to the previous scene/view.
        self.dismiss(animated: true, completion: nil)
    }

    @IBAction func PlayOrPauseMusic(_ sender: UIButton) {
        if isPlaying {
            print("isPlaying")
            AudioManager.sharedInstance.pauseMusic()
            isPlaying = false
            playOrPauseButton.setTitle("Pause", for: .normal)
        } else {
            print("isPaused")
            AudioManager.sharedInstance.startMusic()
            isPlaying = true
            playOrPauseButton.setTitle("Play", for: .normal)
        }
    }
}

/// 使用 Segue 模态呈现从 tableViewController 向 NowPlayingViewController 发送数据。

// define segue to the transition to NowPlaying View
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "playingSong" {
             if let indexPath = self.tableView.indexPathForSelectedRow {
                let controller = segue.destination as! NowPlayingViewController
                if resultSearchController.isActive {
                    controller.playingSong = filteredTableData[indexPath.row]
                } else {
                    controller.playingSong = tableData[indexPath.row]
                }

            }
        }
    }

糟糕,当用户单击此视图中的单元格行时,我发现我的 AudioManger 实际上是通过从 tableViewController 获取 playingSong: SongData 来工作的。我将此 playingSong 声明为静态变量,以便从 AudioManager access/use 它。

现在当用户通过标签栏控制器导航时正在播放音乐,更新我的代码如下。下一步可以在导航视图的 left/right 顶部添加一个按钮来重新呈现播放场景:)

呃,我发现它仍然有问题,当前歌曲的播放和恢复工作正常,但是如果我 select 在 tableViewController 中播放另一首歌曲,那么它仍然会在 NowPlayingView 中播放上一首歌曲。原因可能是 init() 只有一次,所以我需要找到一种方法在 select 另一个单元格行时将值重新分配给 sharedInstance。

import AVKit

class AudioManager {
    // use Singleton pattern keep the music continuing when user move through the App.
    static let sharedInstance = AudioManager()

    var audioPlayer: AVAudioPlayer!
    var playingSong: SongData?

    private init() {
        // config for audio background playing
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
            print("Playback OK")
            try AVAudioSession.sharedInstance().setActive(true)
            print("Session is Active")
        } catch {
            print(error)
        }

        do {
            playingSong = SongsTableViewController.playingSong
            try audioPlayer = AVAudioPlayer(contentsOf: playingSong!.url)
            audioPlayer.prepareToPlay()
        } catch {
            print("could not load file")
        }
    }

    func playMusic() {
       audioPlayer.play()
    }

    func pauseMusic() {
       audioPlayer.pause()
    }

}