Igor Skvortsov/Guide - Tracking Interface Orientation

Created Sun, 13 Oct 2024 00:00:00 +0000 Modified Tue, 08 Jul 2025 22:46:25 +0000
569 Words 3 min

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.