Detecting Device Landscape Orientation in SwiftUI


Greetings, traveler!

When working with adaptive layouts in SwiftUI, one common requirement is to determine whether the device is currently in portrait or landscape mode. While UIKit provides UIDevice.current.orientation, it often produces unreliable results — especially when the device is lying flat. In such cases, values like .faceUp or .faceDown are returned, and UIDeviceOrientation.isLandscape may not reflect the actual screen layout.

A more robust approach in SwiftUI is to analyze the screen’s aspect ratio using GeometryReader. This technique focuses on how content is rendered on screen rather than relying on sensor-based orientation, providing a reliable basis for responsive layout adjustments.

The method involves comparing the width and height of the available geometry space. If width > height, the screen is considered to be in landscape layout. This offers a clear and deterministic way to drive layout logic, regardless of the physical orientation reported by the device.

Below is a simple SwiftUI ViewModifier that exposes this logic through a binding, making it reusable across views.

import SwiftUI

struct DeviceOrientationModifier: ViewModifier {
    @Binding var isLandscape: Bool

    func body(content: Content) -> some View {
        GeometryReader { geometry in
            content
                .onAppear {
                    isLandscape = geometry.size.width > geometry.size.height
                }
                .onChange(of: geometry.size) { newSize in
                    isLandscape = newSize.width > newSize.height
                }
        }
    }
}

extension View {
    func detectDeviceOrientation(isLandscape: Binding<Bool>) -> some View {
        self.modifier(DeviceOrientationModifier(isLandscape: isLandscape))
    }
}

To apply this modifier, simply add it to your root view and provide a state variable to observe orientation changes:

struct ContentView: View {
    @State private var isLandscape = false

    var body: some View {
        VStack {
            Text("Orientation: \(isLandscape ? "Landscape" : "Portrait")")
        }
        .detectDeviceOrientation(isLandscape: $isLandscape)
    }
}

This approach is particularly useful when you need to reorganize UI layouts based on how the screen is being utilized, such as switching between horizontal and vertical stacks, altering grid columns, or toggling visibility of certain interface elements.

By relying on the aspect ratio of the view’s frame rather than the device’s physical orientation, you ensure that your UI reacts appropriately in all scenarios — including when the device is flat on a surface or embedded in a fixed position, such as a stand or dock.