Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
SwiftUI view modifier that performs action whenever accelerometer-based UIDeviceOrientation changes
import CoreMotion
import SwiftUI
struct DeviceOrientationObservingViewModifier: ViewModifier {
let onChange: (UIDeviceOrientation) -> Void
func body(content: Content) -> some View {
content.background(DeviceOrientationObservingView(onChange: onChange))
}
}
extension View {
func onDeviceOrientationChange(perform: @escaping (UIDeviceOrientation) -> Void) -> some View {
modifier(DeviceOrientationObservingViewModifier(onChange: perform))
}
}
private struct DeviceOrientationObservingView: View {
let onChange: (UIDeviceOrientation) -> Void
@StateObject var observer = DeviceOrientationObserver()
var body: some View {
EmptyView().onChange(of: observer.value) { orientation in
onChange(orientation)
}
}
}
private final class DeviceOrientationObserver: ObservableObject {
init() {
manager.startAccelerometerUpdates(to: OperationQueue.main) { [weak self] data, error in
guard error == nil, let acceleration = data?.acceleration else {
self?.value = .unknown
return
}
self?.value = acceleration.deviceOrientation
}
}
@Published var value: UIDeviceOrientation = .unknown
deinit {
manager.stopAccelerometerUpdates()
}
let manager = CMMotionManager()
}
private extension CMAcceleration {
var deviceOrientation: UIDeviceOrientation {
if z <= -0.95 { return .faceUp }
if z >= 0.95 { return .faceDown }
switch atan2(y, -x) {
case -2.25 ... -0.75: return .portrait
case -0.75 ... 0.75: return .landscapeRight
case 0.75 ... 2.25: return .portraitUpsideDown
default: return .landscapeLeft
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment