在 Swift 2 中按下 UINavigation Controller 上的 'back' 按钮让 AVPlayer 继续播放

Let AVPlayer continue playing when pressing the 'back' button on the UINavigation Controller in Swift 2

我正在开发一个广播流媒体应用程序,它可以播放。当按下“主页”按钮和“锁定”按钮时,它也会在后台播放。

应用程序嵌入到 UINavigationController 中,当我按下 UINavigationController 中的“后退”按钮时,它停止播放。 我的问题是:如何让包含 AVPlayerUIViewController 在按下导航控制器中的“后退”按钮时保持活动状态,以便 AVPlayer 继续流式传输?

我的代码

import UIKit
import AVFoundation
import MediaPlayer
import Foundation

class RadioFunctionViewController: UIViewController {

@IBOutlet var playButton: UIButton!
@IBOutlet var statusLabel: UILabel!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false


override func viewDidLoad() {
    super.viewDidLoad()

    setStatus(4)
    getAudioData("http://184.107.179.162:7546/;")
    playingState = true


}

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


@IBAction func buttonPressed(sender: AnyObject)
{
    toggle()
}

func getAudioData(audioURL:String)
{
    player = AVPlayer(URL: NSURL(string: audioURL)!)
    player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.Initial, context: nil)
}

func setStatus(rawValue:Int)
{
    if rawValue == 1
    {
        statusLabel.textColor = UIColor.blueColor()
        statusLabel.text = "Ready for Streaming"
    }else if rawValue == 2
    {
        statusLabel.textColor = UIColor.redColor()
        statusLabel.text = "Failed"

    }else if rawValue == 0
    {
        statusLabel.textColor = UIColor.redColor()
        statusLabel.text = "Failed to load data"
    }else if rawValue == 3
    {
        statusLabel.textColor = UIColor.blueColor()
        statusLabel.text = "Streaming"
    }else if rawValue == 4
    {
        statusLabel.textColor = UIColor.purpleColor()
        statusLabel.text = "Gather data..."
    }
    print("The raw value send is: \(rawValue)")
}

func audioBackgroundPlayback()
{
    do{
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
    }catch {
        print("Could not play audio in the background")
    }

    if (NSClassFromString("MPNowPlayingInfoCenter") != nil)
    {
        let artWorkImage = MPMediaItemArtwork(image: UIImage(named: "ws")!)
        let songInfo2: [String:  AnyObject] = [MPMediaItemPropertyTitle: "Wide Streamings ABC Edition", MPMediaItemPropertyArtist: "Rumbera Network", MPMediaItemPropertyAlbumTitle: "107.9 FM", MPMediaItemPropertyArtwork: artWorkImage]

        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = songInfo2
        UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
    }


}

func toggle()
{
    if playButton.titleLabel?.text == "Play"
    {
        print("The play option is chosen")
        playRadio()
    }else{
        print("The pause option is chosen")
        pauseRadio()
    }
}

func playRadio()
{
    player.play()
    setStatus(3)
    playButton.setTitle("Pause", forState: UIControlState.Normal)
    audioBackgroundPlayback()
}

func pauseRadio()
{
    player.pause()
    playButton.setTitle("Play", forState: UIControlState.Normal)
}

override func remoteControlReceivedWithEvent(event: UIEvent?) {
    if event?.type == UIEventType.RemoteControl
    {
        if event?.subtype == UIEventSubtype.RemoteControlPlay
        {
            toggle()
        }else if event?.subtype == UIEventSubtype.RemoteControlPause
        {
            pauseRadio()
        }else if event?.subtype == UIEventSubtype.RemoteControlTogglePlayPause
        {
            toggle()
        }
    }
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>)
{
    if (keyPath!.containsString("status"))
    {
        if player.status == AVPlayerStatus.ReadyToPlay
        {
            player.prerollAtRate(0.001, completionHandler: {(succes:Bool)-> Void in

                if succes{
                    self.setStatus(1)
                    self.setStatus(3)
                    self.playRadio()

                }else{
                    self.setStatus(1)
                    self.setStatus(2)
                }

            })
        }else if player.status == AVPlayerStatus.Failed{
            self.setStatus(2)

        }else if player.status == AVPlayerStatus.Unknown
        {
            self.setStatus(0)
        }

    }
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.navigationController?.navigationBarHidden = false
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.navigationController?.navigationBarHidden = false
    if playingState == true
    {
        audioBackgroundPlayback()
        player.removeObserver(self, forKeyPath: "status")
        print("The AVPlayer is playing in background")
    }else{
        player.removeObserver(self, forKeyPath: "status")
        print("The view Dissapear")
    }


}

希望有人能帮我解决这个问题 提前致谢

这里的问题是播放器嵌入在你的控制器中

var player:AVPlayer = AVPlayer()

因此,当您按下后退按钮时,控制器会弹出并解除分配,您的播放器也会随之弹出。 您需要做的是将播放器 属性 放在其他地方(例如 AppDelegate、自定义导航控制器)以保留对其的引用,使其保持活动状态。

AVPlayer 只要处于活动状态就会继续工作。一旦引用 UIViewController 通过弹回释放 AVPlayer 也将从内存中丢弃。

我建议您创建一个单例播放器 class 并在其中为 start/stop/play/pause AVPlayer 创建 API。现在,您可以从应用程序的任何位置全局访问它。

编辑: 为了方便 OP(示例开始):

class MyAVPlayer {
    static let sharedInstance = MyAVPlayer()

    var player:AVPlayer = AVPlayer()

    func play() {
        // Put play code here
    }

    func playWithURL(url : NSURL) {
        // Put play code here
    }
}

像这样调用它(在您的应用程序中的任何地方):

MyAVPlayer.sharedInstance.playWithURL(myURL)

我设法通过对 AppDelegate Class 进行一些修改来修复它。

修改成AppDelegate:

var player:AVPlayer = AVPlayer()
internal var avPlayerUpdateNotification = NSNotificationCenter.defaultCenter()
let notificationStateupdate = "RadioStationChangeUpdate"
let radioStationChangeNSString:NSString = "RadioStationChangeNotification"
private var isPlaying:Bool = false
private var selectedRadioStation:String = ""

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
    // Override point for customization after application launch.
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "setNewSelectedRadioStation:", name: radioStationChangeNSString as String, object: nil)

    return true
}

已添加到AppDelegate中的其他功能Class

func streamAudio(audioLink:String)
{
    player = AVPlayer(URL: NSURL(string: audioLink)!)
    player.play()
    isPlaying = true
}

func play()
{
    player.play()
    isPlaying = true
}

func pause()
{
    player.pause()
    isPlaying = false
}

func getPlayerState() -> Bool
{
    return isPlaying
}

func setCurrentSelectedRadioStation(selectedStation:String)
{
    self.selectedRadioStation = selectedStation
}

func getCurrentSelectedRadioStation() -> String
{
    return selectedRadioStation
}

func setNewSelectedRadioStation(notification: NSNotification)
{
    let radioStation = notification.object!

    if (radioStation.containsString("Rumbera network"))
    {
        if(selectedRadioStation == radioStation as! String)
        {
            print("Rumbera Network is already playing")
        }else{
            print("Rumbera Network is selected in AppDelegate")
            streamAudio("http://184.107.179.162:7546/;")
            setCurrentSelectedRadioStation(radioStation as! String)
        }
    }else if (radioStation.containsString("RocKorsow"))
    {
        if(selectedRadioStation == radioStation as! String)
        {
            print("RocKorsow is already playing")
        }else{
            print("RocKorsow is selected in AppDelegate")
            streamAudio("http://youngfreshfast.serverroom.us:9166")
            setCurrentSelectedRadioStation(radioStation as! String)
        }
    }else if (radioStation.containsString("Pause"))
    {
        pause()
    }else if (radioStation.containsString("Play"))
    {
        play()
    }else{
        print("Nothing is found")
    }

}

处理AVPlayer的class:

import UIKit
import AVFoundation
import MediaPlayer
import Foundation

class RadioFunctionViewController: UIViewController {
@IBOutlet var playButton: UIButton!
var player:AVPlayer = AVPlayer()
private let ObservatingKeyPath = "currentItem.status"
private let PlayerStatusObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
private var playingState:Bool = false
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

override func viewDidLoad() {
    super.viewDidLoad()
    checkPlayerCurrentState()
}

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


@IBAction func performAction(sender: UIButton)
{
    let buttonLabel = (sender.titleLabel?.text)!

    switch buttonLabel
    {
        case "Play":
        print("The AVPlayer is playing")
        NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Play"))
        playButton.setTitle("Pause", forState: UIControlState.Normal)
        case "Pause":
        print("The AVPlayer is pause")
        NSNotificationCenter.defaultCenter().postNotificationName("RadioStationChangeNotification", object: NSString(string: "Pause"))
        playButton.setTitle("Play", forState: UIControlState.Normal)
    default:
        break

    }
}

func checkPlayerCurrentState()
{
    let player_state = getPlayerState()
    if player_state == true
    {
        print("The AVPlayer is playing")
        playButton.setTitle("Pause", forState: UIControlState.Normal)
    }else{
        print("The AVPlayer is not playing")
        playButton.setTitle("Play", forState: UIControlState.Normal)
    }
}

func getPlayerState() -> Bool
{
    let state = appDelegate.getPlayerState()
    return state
}

func audioBackgroundPlayback()
{
    do{
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
    }catch {
        print("Could not play audio in the background")
    }
}

希望我的解决方案可以帮助遇到同样问题的人。 感谢大家的反馈。