簡體   English   中英

我無法在后台播放音樂(Xcode 9、Swift 4)

[英]I cannot play music in the background (Xcode 9, Swift 4)

正如問題所暗示的那樣,我需要我的應用程序像大多數音樂播放器一樣在后台播放音樂。 我試圖在網上找到一些東西,但它們不起作用。 有人可以看看我的問題嗎? 我認為蘋果可能會在后台自動殺死我的應用程序,因為它可以在后台播放幾秒鍾(可能像 10 秒)。

此外,我想讓音樂在另一個視圖控制器中播放。 現在,每當我 go 回到我的根視圖控制器時,音樂總是停止。

這是我的代碼。

//
//  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

    // 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()
        }
    }
}

在這個問題上花了幾天后,我終於弄明白了。 在 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:.
    }
}

更新:Swift 4.2

這對我也有幫助:

1. 第一步:
在此處輸入圖片說明

2. 第二:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    let session = AVAudioSession.sharedInstance()
    do{
        try session.setActive(true)
        try session.setCategory(.playback, mode: .default,  options: .defaultToSpeaker)
    } catch{
        print(error.localizedDescription)
    }
    return true
}

打開項目,繼續功能並啟用背景模式,然后選擇音頻、隔空播放和畫中畫 它將在您的Info.plist文件中為后台音頻添加一個鍵。

在此處輸入圖片說明

對我來說,我的應用程序在我的應用程序中有權利“NSFileProtectionComplete”。 這導致音頻在應用程序進入后台后不久停止播放。

使用此設置,如果您的應用程序導入音頻文件(而不是與應用程序捆綁在一起),那么您需要編寫文件以便它們有在后台播放的權限,否則默認情況下文件都會有“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

我最近遇到了同樣的事情。 我希望我的音樂在后台播放,下一首歌曲將在一首歌曲結束時播放。 終於,我找到了解決辦法!!

let session = AVAudioSession.sharedInstance()
do {
      try audioSession.setActive(true)
        
      if #available(iOS 12.0, *) {
          try audioSession.setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.voicePrompt)
      } else {
          try audioSession.setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.default)
      }
        
      UIApplication.shared.beginReceivingRemoteControlEvents()
  } catch {
      print("AVAudioSessionCategoryPlayback failed.")
 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM