I recently stumbled across a bug in the AVPlayerViewController class. If you’re not familiar with this AVKit class, it’s a handy object used to play videos in iOS.
The bug happens when you use custom playback controls. Under some circumstances, your controls will stop responding. The bug is fixed in the upcoming iOS 13, but the app I was developing will be used in devices that currently have iOS 12 and can’t be upgraded. So finding a solution was our only option. It turned out that solution was easy to implement. Let me explain the details of the bug and how to fix it.
System-supplied and custom playback controls
By default, an AVPlayerViewController object shows system-supplied playback controls. You can combine or replace these controls with your own custom controls.
The showsPlaybackControls property of AVPlayerViewController objects controls whether you want the system-supplied playback controls.
To provide your custom controls, access the contentOverlayView property and add your views using addSubview
Custom controls not responding
For my case, I needed the following:
1-Start playback with custom playback controls.
2-At some point, hide the custom controls and show the system-supplied playback controls.
3-After some time, go back to the custom playback controls.
The problem was that, after hiding the system-supplied playback controls (point 3, above), the custom controls stopped working. See the video below for more details.
The view hierarchy explains it all
If you examine the view hierarchy you will find that the system-supplied playback controls are contained in a view of type AVPlaybackControlsView (this class is private and was introduced in iOS 11).
In iOS 12, starting playback with showsPlaybackControls = false will result in an object with no AVPlaybackControlsView view, as shown in Image 1 below.
If you later set showsPlaybackControls = true, a view of type AVPlaybackControlsView will be created. However, if later on you deactivate the system-supplied playback controls using showsPlaybackControls = false, its view will stay at the top of the hierarchy, thus preventing input from reaching your custom controls. See Image 2 below.
Since an AVPlaybackControlsView view is blocking the input, we can either remove it or hide/disable it.
I have found that, if removed, it won’t be added again when setting showsPlaybackControls = true. So the best fix simply consists in hiding/disabling it. Of course, if later on you want to show the system controls again, you will need to unhide/enable it.
The video below shows the result after the above fix is applied.
A demo project
You can download a project which implements the fix.