我无法在后台播放音乐(XCode 9,Swift 4)

时间:2018-07-02 02:29:20

标签: ios swift avfoundation swift4 avaudioplayer

正如问题所暗示的,我需要我的应用像大多数音乐播放器一样在后台播放音乐。我试图在网上找到一些东西,但是它们不起作用。有人可以看看我的问题吗?我认为苹果可能会在后台自动杀死我的应用程序,因为它可以在后台播放几秒钟(大概10秒钟)。

此外,我想让音乐在另一个viewController中播放。现在,每当我回到根目录viewController时,音乐始终会停止。

这是我的代码。

//
//  MusicLibraryTableViewController.swift
//  WF
//
//  Created by Bo Ni on 7/1/18.
//  Copyright © 2018 Bo Ni. All rights reserved.
//

import UIKit
import AVFoundation

class MusicLibraryTableViewController: UITableViewController, AVAudioPlayerDelegate{

let songs: [String] = ["After Master"]

let producer: [String] = ["August Wu/Zoro"]

let identifier = "musicIdentifier"

var audioPlayer: AVAudioPlayer?

var isAudioPlayerPlaying = false

override func viewDidLoad() {
    super.viewDidLoad()

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem
}

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

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, cellForRowAt
    indexPath: IndexPath) -> UITableViewCell {

    let cell: MusicTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: identifier) as! MusicTableViewCell
    cell.producerLabel.text = producer[indexPath.row]
    cell.musicNameLabel.text = songs[indexPath.row]
    cell.playButton.setImage(UIImage(named:"Play Button")?.withRenderingMode(.alwaysOriginal), for: UIControlState.normal)

    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 64.0
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return songs.count
}

override func tableView(_ tableView: UITableView,
    didSelectRowAt indexPath: IndexPath) {

    let cell: MusicTableViewCell = self.tableView.cellForRow(at: indexPath) as! MusicTableViewCell

    let music = NSURL.fileURL(withPath: Bundle.main.path(forResource: songs[indexPath.row], ofType: "mp3")!)
    do {
        try audioPlayer = AVAudioPlayer(contentsOf: music)
    } catch{
        print(error.localizedDescription)
    }

    UIApplication.shared.beginReceivingRemoteControlEvents()

    audioPlayer?.delegate = self
    audioPlayer?.prepareToPlay()

    if isAudioPlayerPlaying == true{
        stopAudio()
        isAudioPlayerPlaying = false
        cell.playButton.setImage(UIImage(named: "Play Button"), for: UIControlState.normal)
    }else{
        prepareAudio()
        playAudio()
        isAudioPlayerPlaying = true
        cell.playButton.setImage(UIImage(named: "Stop Button"), for: UIControlState.normal)
        }

}

func prepareAudio(){
    do {
        //keep alive audio at background
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
    } catch _ {
    }
    do {
        try AVAudioSession.sharedInstance().setActive(true)
    } catch _ {
    }
}

func playAudio(){
    if let player = audioPlayer{
        player.play()
    }
}

func stopAudio(){
    if let player = audioPlayer{
        player.stop()
    }
}


}

3 个答案:

答案 0 :(得分:1)

打开项目,打开功能并启用背景模式,然后选择音频,AirPlay和画中画。它将在您的Info.plist文件中添加一个键,以在后台播放音频。

enter image description here

答案 1 :(得分:0)

对我来说,我的应用程序在我的应用程序中有权利“NSFileProtectionComplete”。这导致音频在应用进入后台后不久停止播放。

有了这个设置,如果你的app导入了音频文件(而不是和app捆绑在一起),那么你需要写文件让他们有后台播放的权限,否则文件默认都会有“completeFileProtection” " 并且 AVAudioPlayer 在进入后台后不久将无法解码您的文件。

try data.write(to: fileURL, options: .completeFileProtectionUnlessOpen)

来自: https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/encrypting_your_app_s_files

答案 2 :(得分:-1)

在这个问题上花了几天后,我终于明白了。在Swift 4中,我认为我们需要在项目目录下的appDelegate类中添加几行代码。像这样:

import UIKit
import AVFoundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let session = AVAudioSession.sharedInstance()
    do{
        try session.setActive(true)
        try session.setCategory(AVAudioSessionCategoryPlayback)
    } catch{
        print(error.localizedDescription)
    }
    return true
}

func applicationWillResignActive(_ application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

}