When using multiple AVPlayerLayer instances connected to one AVPlayer instance in different view controllers the video freezes on simulators. Unless each AVPlayerLayer that becomes "invisible" after a view controller transition (e.g. viewDidDisappear) is detached from the player. This approach without detaching works fine on iOS devices.
Steps:
- Download the sample project from https://github.com/KaiOelfke/MultiplePlayerLayers
- Run on an iOS simulator
- Switch to the second tab
Expectation: Video is playing.
Actual results: Video frame is frozen. Audio plays. Switching tabs results in a few frames being played before freezing again.
- Run on an iOS device
- Switch to the second tab
- Switch tabs a few times
The app behaves as expected. Both player views play the video normally.
The different behavior on the simulator can be fixed by detaching the player from the AVPlayerLayer in ViewController.viewDidDisappear(). It seems that, if at least one attached player view is regarded as "invisible" by iOS the video stops to play on the simulator.
The usecase is an app with multiple tabs and a miniplayer bar (including a small video layer) on top of the tab bar similar to the Apple Music app plus a modally shown full screen player view. Each tab has its own miniplayer AVPlayerLayer.
Is this a simulator bug? What's the recommended way to implement multiple AVPlayerLayer instances in various parts of the view hierarchy?
// ViewController.swift
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// as soon as the second VC becomes visible and the player view is attached the issue occurs
VideoController.shared.attachPlayerView(playerView)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// detaching here solves the issue
// VideoController.shared.detachPlayerView(playerView)
}
//VideoController.swift
func attachPlayerView(_ playerView: PlayerView) {
playerView.player = player
}
func detachPlayerView(_ playerView: PlayerView) {
playerView.player = nil
}
//PlayerView.swift
var player: AVPlayer? {
get {
playerLayer?.player
}
set {
playerLayer?.player = newValue
}
}