The short guide on how to track the interface orientation in SwiftUI.
Task
Track the interface orientation for the current view in order to implement layout correctly.
Solution
Orientation tracking class:
public final class IsPortraitOrientationBroadcaster: ObservableObject {
public static let shared = IsPortraitOrientationBroadcaster()
@Published var isInterfacePortrait: Bool = true
private var interfaceOrientation: UIInterfaceOrientation? {
(UIApplication.shared.connectedScenes.first as? UIWindowScene)?.interfaceOrientation
}
private init() {
// Set initial orientation based on the interface orientation
if let interfaceOrientation {
isInterfacePortrait = interfaceOrientation.isPortrait
}
// Observe orientation changes based triggered by device orientation change
NotificationCenter.default.addObserver(
forName: UIDevice.orientationDidChangeNotification,
object: nil,
queue: .main
) { [weak self] _ in
guard let interfaceOrientation = self?.interfaceOrientation else { return }
self?.isInterfacePortrait = interfaceOrientation.isPortrait
}
}
}
Property wrapper for convenient use:
@propertyWrapper public struct IsPortraitOrientation: DynamicProperty {
@ObservedObject private var broadcaster: IsPortraitOrientationBroadcaster = .shared
public init() {}
public var wrappedValue: Bool {
broadcaster.isInterfacePortrait
}
}
How to Apply
You can now easily use this Property Wrapper in SwiftUI to manage the interface based on orientation:
struct ContentView: View {
@IsPortraitOrientation var isPortrait
var body: some View {
Text(isPortrait ? "Portrait" : "Album")
}
}
What’s Happening Here?
Orientation
When tracking device orientation in iOS, it’s important to understand the difference between UIWindowScene.interfaceOrientation and UIDeviceOrientation.
UIWindowScene.interfaceOrientation
What It Is: The interface orientation of the app, reflecting the current position of the interface relative to the device screen.
How to obtain it: UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
Note that there can be multiple scenes in an app, so the orientation must be used for the specific scene.
When Available: Available in the context of the active window of the app (scene), rather than the entire device.
Pros
- Always available right after the app starts, which is useful if you need to ensure the correct interface state from the very beginning
- Reflects the current state of the interface and is used to manage interface changes when the orientation switches (for example, switching between portrait and landscape mode)
- Remains stable if the interface orientation is locked, regardless of the physical orientation of the device
Be aware
- May not match the interface orientation if, for example, the interface is locked in portrait orientation but the device is in landscape orientation
UIDeviceOrientation
What It Is: The physical orientation of the device, tracked by system hardware sensors.
How to obtain it: UIDevice.current.orientation
When Available: Always available, but there are nuances regarding how quickly it updates.
Pros
- Reflects the physical position of the device. Useful in cases where it’s important to account for the actual movements of the device, such as when working with the camera.
Be aware
- After the app starts, it may return a value of .unknown, as the orientation only updates after a physical change in device position. Therefore, it’s not always reliable at app launch
- May not match the interface orientation if, for example, the interface is locked in portrait orientation but the device is in landscape orientation
Important Note
There are examples on the internet of mapping UIDeviceOrientation to UIInterfaceOrientation and vice versa. This approach can lead to inconsistencies, especially if the interface is locked or if data about the physical orientation of the device has not yet updated.
Property Wrapper
Using a Property Wrapper is not mandatory but simplifies the process, making the code more concise and convenient. In this example, @IsPortraitOrientation automatically tracks orientation changes, saving you from manually updating the state.