在 Swift 2 中按下 UINavigation Controller 上的 'back' 按钮让 AVPlayer 继续播放
Let AVPlayer continue playing when pressing the 'back' button on the UINavigation Controller in Swift 2
我正在开发一个广播流媒体应用程序,它可以播放。当按下“主页”按钮和“锁定”按钮时,它也会在后台播放。
应用程序嵌入到 UINavigationController
中,当我按下 UINavigationController 中的“后退”按钮时,它停止播放。
我的问题是:如何让包含 AVPlayer
的 UIViewController
在按下导航控制器中的“后退”按钮时保持活动状态,以便 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")
}
}
希望我的解决方案可以帮助遇到同样问题的人。
感谢大家的反馈。
我正在开发一个广播流媒体应用程序,它可以播放。当按下“主页”按钮和“锁定”按钮时,它也会在后台播放。
应用程序嵌入到 UINavigationController
中,当我按下 UINavigationController 中的“后退”按钮时,它停止播放。
我的问题是:如何让包含 AVPlayer
的 UIViewController
在按下导航控制器中的“后退”按钮时保持活动状态,以便 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")
}
}
希望我的解决方案可以帮助遇到同样问题的人。 感谢大家的反馈。