Skip to navigation

Rotate video for fullscreen

Learn how to control fullscreen interface rotation, observe what the system actually did, and choose how the picture fills the frame in the FastPix iOS Player.

The FastPix iOS Player SDK rotates the interface for fullscreen playback and reports truthfully what came of the request. It does not rotate the video layer independently of the interface. It asks the window scene for an orientation, reads back what the system actually settled on, and tells you, including every case where nothing moved.

Rotation is requested through UIWindowScene.requestGeometryUpdate, a public API with no key-value coding and no deprecated calls. onFullscreenOrientationChanged(isLandscape:) fires only when the interface really moved, and reports the orientation it settled in, not the one that was asked for. Every request, granted or refused, is reported on onFullscreenOrientationRequest(outcome:), and each refusal explains itself and says what to do instead. Auto-rotation is opt-out per player. Independently of rotation, videoFillMode decides how the picture is fitted into whatever shape the frame ends up.

To update your own custom layout as the device rotates, see Handle orientation changes.

Control auto-rotation

Auto-rotation is on by default. Disable it per player when your own view controller owns orientation:

playerViewController.setupFullscreen(parent: self, container: playerView)
// On by default. Disable when your own view controller owns orientation.
playerViewController.setFullscreenAutoRotate(enabled: false)

Observe what actually happened

Conform to FastPixFullscreenDelegate. onFullscreenOrientationChanged(isLandscape:) fires only when the interface orientation really changed, while onFullscreenOrientationRequest(outcome:) fires for every request, including the refusals:

extension VideoPlayerViewController: FastPixFullscreenDelegate {
// Fires ONLY when the interface orientation really changed.
func onFullscreenOrientationChanged(isLandscape: Bool) {
layoutChrome(forLandscape: isLandscape)
}
// Fires for EVERY request, including the refusals.
func onFullscreenOrientationRequest(outcome: FastPixOrientationOutcome) {
print("[Orientation]", outcome.explanation)
if case .rejectedBySystem = outcome {
// Almost always: supportedInterfaceOrientations excludes the requested orientation.
showManualRotateHint()
}
}
}

FastPixOrientationOutcome

The outcome is deliberately not a Bool. “It worked”, “there is no window scene to ask”, and “the system refused” need different responses, and collapsing them into a flag leads to reporting rotations that never happened.

CaseMeaning
.applied(isLandscape:)The system accepted the request and the interface is now in that orientation
.alreadyInRequestedOrientation(isLandscape:)Nothing needed to change, so no rotation animation occurred and chrome has nothing to do
.notRequestedAuto-rotation is off via setFullscreenAutoRotate(enabled: false). You asked for this
.unavailableBeforeIOS16requestGeometryUpdate is iOS 16+. On earlier versions the only mechanism was private API, which this SDK does not use
.noWindowSceneThe player’s view is not in a window. Call this after the view has been added to one
.rejectedBySystem(reason:)The system was asked and declined, usually because your supportedInterfaceOrientations does not include it. The SDK cannot and does not override that
PropertyTypeDescription
didRotateBoolTrue only for .applied
isLandscapeBool?The orientation at the end of this, whether or not it just moved. nil for every refusal
explanationStringA loggable sentence, including the supported alternative for each refusal

Fill the rotated frame

Rotation changes the shape of the space the picture is drawn in, which makes fitting the next question. videoFillMode is applied to both surfaces the SDK can present through, the AVPlayerViewController’s own video gravity and the AVPlayerLayer attached for Picture in Picture, so a mode does not appear to take effect and then silently revert when Picture in Picture starts. It also survives the surface being rebuilt.

playerViewController.videoFillMode = .fill
if playerViewController.videoFillMode.cropsPicture {
// Burned-in subtitles along the lower edge can be cropped away in this mode.
print(playerViewController.videoFillMode.explanation)
}

FastPixVideoFillMode

CaseAVLayerVideoGravityMeaning
.fit.resizeAspectDefault. The whole picture, undistorted, with bars where the shapes differ
.fill.resizeAspectFillFrame filled, nothing distorted, edges cropped away, including subtitles burned into them
.stretch.resizeFrame filled, nothing cropped, picture distorted to match the frame’s shape

Derived properties: preservesAspectRatio (false only for .stretch), cropsPicture (true only for .fill), and explanation. FastPixVideoFillMode.mode(for:) maps a gravity constant back, returning nil rather than a confident wrong answer if AVFoundation adds a fourth value.

Note:

  • The SDK requests an orientation; it cannot impose one. Your view controller’s supportedInterfaceOrientations is the final authority, and .rejectedBySystem is what you get when it disallows the request.
  • On iOS 13 to 15, fullscreen does not rotate the interface (.unavailableBeforeIOS16). Drive rotation from your own supportedInterfaceOrientations instead, which works on every version, 16 and later included.
  • Call rotation-dependent APIs after the view is in a window. Off-window there is no scene to ask, which is .noWindowScene and a call-ordering mistake rather than a device limitation.
  • videoFillMode is presentation only. It does not change the decoded frames, the asset, or what any analytics or screenshot API reports.
  • Add overlay views such as subtitle labels and custom controls to playerViewController.view rather than self.view, so they stay positioned correctly across rotations and fullscreen transitions.

Frequently asked questions

How does the FastPix iOS Player rotate video for fullscreen?

The SDK rotates the interface by asking the window scene for an orientation through UIWindowScene.requestGeometryUpdate, a public iOS 16+ API, then reads back the orientation the system actually settled on. It does not rotate the video layer independently of the interface, and it does not use private API. Auto-rotation is on by default and can be turned off per player with setFullscreenAutoRotate(enabled: false).

Why won't the FastPix iOS Player rotate to landscape?

Your view controller’s supportedInterfaceOrientations is the final authority, and the SDK cannot override it. When the system declines a request, the outcome is .rejectedBySystem(reason:), usually because the requested orientation is not in supportedInterfaceOrientations. Observe onFullscreenOrientationRequest(outcome:) to see the reason and show a manual-rotate hint.

Does fullscreen rotation work on iOS 15 and earlier?

No. requestGeometryUpdate is iOS 16 and later, so on earlier versions the outcome is .unavailableBeforeIOS16. The only earlier mechanism was private API, which this SDK does not use. Drive rotation from your own supportedInterfaceOrientations instead, which works on every version.

What is the difference between fit, fill, and stretch video fill modes?

.fit (the default) shows the whole picture undistorted with bars where the shapes differ. .fill fills the frame with nothing distorted but crops the edges away, including subtitles burned into them. .stretch fills the frame with nothing cropped but distorts the picture to match the frame’s shape. videoFillMode is presentation only and does not change the decoded frames or what analytics report.