How to detect device rotation
How to detect device rotation êŽë š
Updated for Xcode 15
SwiftUI doesn't have a built-in way to detect the user rotating their device between portrait and landscape orientation, but we can make one using a custom modifier by responding to the UIDevice.orientationDidChangeNotification
notification.
This takes three steps:
- Creating a custom view modifier that watches for orientation changes and runs a callback function when it happens. It's not required, but we're going to make the callback accept a
UIDeviceOrientation
as its only parameter, just in case you need to know the current orientation. - Wrapping that view modifier up in a
View
extension so that it's easier to call. - Using your custom modifier in a view of your choosing.
Important
At the time of writing view modifiers do not work with onReceive()
unless you first add onAppear()
, which is why it appears above. Yes, it's empty, but it acts as a workaround for the problem.
Here's a complete code sample:
// Our custom view modifier to track rotation and
// call our action
struct DeviceRotationViewModifier: ViewModifier {
let action: (UIDeviceOrientation) -> Void
func body(content: Content) -> some View {
content
.onAppear()
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
action(UIDevice.current.orientation)
}
}
}
// A View wrapper to make the modifier easier to use
extension View {
func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
self.modifier(DeviceRotationViewModifier(action: action))
}
}
// An example view to demonstrate the solution
struct ContentView: View {
@State private var orientation = UIDeviceOrientation.unknown
var body: some View {
Group {
if orientation.isPortrait {
Text("Portrait")
} else if orientation.isLandscape {
Text("Landscape")
} else if orientation.isFlat {
Text("Flat")
} else {
Text("Unknown")
}
}
.onRotate { newOrientation in
orientation = newOrientation
}
}
}
Tips
This may not work while your app is connected to Xcode's debugger â try pushing your app to a real device, then running it manually rather than via Xcode.
Please remember that device orientation isn't quite as useful as you might expect. Yes, on iPhone a landscape orientation means you have more horizontal space than vertical, but on iPad it's possible for your app to be running in landscape while in split-screen mode â technically the whole screen still has a larger width than height, but the actual space allocated to our app is only a small slice of that width.