I want to detect device orientation in swift so that I can apply the correct orientation to a video. The code I have for updating device orientation works correctly but is extremely sensitive to tiny movements. Holding the device upward and barely tilting it causes the device orientation to update to a landscape orientation even though the phone is basically still portrait. Is there a way to lower the sensitivity for device orientation so that tiny movements dont cause an unwanted update?
struct TopVideo: View {
var body: some View {
ZStack {
}
.onRotate { newOrientation in
withAnimation(.easeInOut(duration: 0.2)){
orien = newOrientation
}
}
}
}
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)
}
}
}
extension View {
func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
self.modifier(DeviceRotationViewModifier(action: action))
}
}
2
Answers
To expand on matt‘s comment to watch the app orientation instead of the device orientation, you can modify your approach to observe changes in the interface orientation.
That would focus on the orientation of the app’s user interface, which might change less frequently than the device’s physical orientation, thus providing a less sensitive detection mechanism.
Modify your
DeviceRotationViewModifier
to observe changes in thestatusBarOrientation
of the app’s window scene. That requires access to the currentUIWindowScene
to observe itsinterfaceOrientation
.Make sure your
onRotate
modifier now triggers actions based on the app’s interface orientation.The
AppOrientationViewModifier
now listens for theUIApplication.didChangeStatusBarOrientationNotification
notification. When the orientation changes, it retrieves the currentUIInterfaceOrientation
from theUIWindowScene
and triggers the provided action.The extension method
onRotate
is adapted to accept a closure with aUIInterfaceOrientation
parameter, reflecting the change to app interface orientation detection.Again, this assumes your app uses
UIWindowScene
(which is standard for apps targeting iOS 13 and later). If you are supporting older versions of iOS, you might need to adjust the implementation to make sure compatibility.As noted by lorem ipsum in the comments, you could also use:
ViewThatFits
, which automatically chooses between multiple views based on which one fits the available space best. This is particularly useful in situations where the layout needs to adapt significantly between portrait and landscape orientations without explicitly tracking the orientation state.SwiftUI.Layout
, introduced in iOS 16, where you can define more complex custom layouts that adapt to the container’s size. It offers a more powerful and flexible way to create adaptive UIs that can respond to changes in orientation, screen size, and other factors.You just need to put a
GeometryReader
around your content, to measure the size of the view. This approach works with iPad split screen too.At the simplest level, you could pass a flag like
isLandscape
to your top-level view. The view can then base its layout on this flag. If you really need to perform some kind of action when the orientation changes, an.onChange
callback can be used:Be aware the two-thirds split-screen on iPad is pretty close to square on some devices. So instead of passing a boolean flag, it might be more useful to pass the actual view size and maybe the safe area insets too. You might like to use a struct to encapsulate this information. This struct could then provide some read-only computed properties: