Opei Opei - 17 days ago 10
Swift Question

AVPlayer Freezes once moved to background

I have the following code that loads a video and loops it. However, when pressing home and returning to the application the video freezes and will not play again. I feel like the answer is in the AppDelegate functions but can not seem to get them to work.

import UIKit
import AVKit
import AVFoundation



class ViewController: UIViewController, UIApplicationDelegate {



var videoPlayer: AVPlayer!
var playerLayer: AVPlayerLayer?



override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("video", ofType:"mp4")
let url = NSURL(fileURLWithPath: path!)
let playerItem = AVPlayerItem(URL: url)

self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.videoPlayer!.play()

self.view.layer.addSublayer(self.playerLayer!)


NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}


func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}


func applicationWillResignActive(application: UIApplication) {
videoPlayer.pause()
}


func applicationDidBecomeActive(application: UIApplication) {
videoPlayer.play()
}


func applicationDidEnterBackground(application: UIApplication) {
videoPlayer.pause()
}


func applicationWillEnterForeground(application: UIApplication) {
videoPlayer.play()
}





override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}



}


Here is what I used to fix it.

import UIKit
import AVKit
import AVFoundation



class ViewController: UIViewController, UIApplicationDelegate {



var videoPlayer: AVPlayer!
var playerLayer: AVPlayerLayer?



override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("video", ofType:"mp4")
let url = NSURL(fileURLWithPath: path!)
let playerItem = AVPlayerItem(URL: url)

self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.videoPlayer!.play()

self.view.layer.addSublayer(self.playerLayer!)

NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("continueVideo:"), name: "continueVideo", object: nil)


NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}


func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}

func continueVideo(notification: NSNotification) {
self.videoPlayer.play()
}

func applicationWillResignActive(application: UIApplication) {
videoPlayer.pause()
}


func applicationDidBecomeActive(application: UIApplication) {
videoPlayer.play()
}


func applicationDidEnterBackground(application: UIApplication) {
videoPlayer.pause()
}


func applicationWillEnterForeground(application: UIApplication) {
videoPlayer.play()
}





override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}



}


And the app delegate portion

import UIKit


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

return true
}

func applicationWillResignActive(application: UIApplication) {


}

func applicationDidEnterBackground(application: UIApplication) {


}

func applicationWillEnterForeground(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName ("continueVideo", object:nil)
}

func applicationDidBecomeActive(application: UIApplication) {

}

func applicationWillTerminate(application: UIApplication) {

}


}

Answer

This is a simple fix, you need to create a NSNotification and call it from your AppDelegate:

func applicationWillEnterForeground(application: UIApplication) {
     NSNotificationCenter.defaultCenter().postNotificationName("continueVideo", object: nil)
}

SWIFT3 Update

func applicationWillEnterForeground(_ application: UIApplication) {
    // NOTE: Notification.Name raw value should be global 
    NotificationCenter.default.post(name: Notification.Name(rawValue: "com.yourappname.continueVideo"), object:self)
}

And the receive that notification in the normal way:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "<funcName>:", name: "continueVideo", object: nil)

SWIFT3 Update

NotificationCenter.default.addObserver(self, selector: #selector(<funcName>), name: Notification.Name(rawValue: "com.yourappname.continueVideo"), object: nil)