Using Core Motion within a SwiftUI application
Understand how to use sensor data in your SwiftUI app using the Core Motion framework.
To access a device’s accelerometer, gyroscopes, and, when available, the pedometer, magnetometer, and barometer sensors, whether on iOS, iPadOS, watchOS, or visionOS, use the Core Motion framework. This framework reports motion and environment data from the integrated hardware sensors. Using it within an app created with SwiftUI to create visual interaction based on the collected data is quite simple.
Keep in mind that to test the features of an app that uses the Core Motion framework, you must run the application on the device. In the following example, we are simulating dynamic depth using the gyroscope data to rotate the UI elements and move their shadow.
Step 1 - Movement management
The first step is to import the Core Motion framework.
import SwiftUI
import CoreMotion
Then, create a class responsible for providing the data from the device sensors. The class MotionDataProvider, conforming with the ObservableObject protocol, will store the motion data from the device sensor and publish any updates based on the device's movement.
class MotionDataProvider: ObservableObject {
// 1.
private let motionProvider = CMMotionManager()
// 2.
@Published var x = 0.0
@Published var y = 0.0
init() {
// 3.
motionProvider.deviceMotionUpdateInterval = 1 / 20
motionProvider.startDeviceMotionUpdates(to: .main) { [weak self] data, error in
// 4.
guard let motion = data?.attitude else { return }
self?.x = motion.roll
self?.y = motion.pitch
}
}
}
- The
motionProviderconstant is an instance ofCMMotionManager, the object responsible for starting and managing motion services. - The properties
xandywill be updated by the motion manager object, they are tagged with the@Publishedproperty wrapper. - When initializing the
MotionDataProviderobject, set up the motion manager object:.deviceMotionUpdateInterval: An instance property that provides device motion updates at regular intervals, in seconds, determined by the received value..startDeviceMotionUpdates: An instance method to get the latest device motion data without a block handler.
To update the UI, the.mainparameter indicates that the data will be processed in the main queue and[weak self]ensures that if not needed, the current object is not kept in memory, avoiding retain cycles.
Thedataanderrorvalues contain information about the device. The data and any possible errors found, respectively.
- Check if there is data to be read and access the
attitudevalue, which contains the orientation of the device relative to a reference frame at a point in time, of the typeCMAttitude. Therollvalue is assigned to thexproperty, representing a rotation around a longitudinal axis. Thepitchvalue is assigned to theyproperty, representing a rotation around a lateral axis.
Step 2 - Create movement in the view
In a SwiftUI view, let’s use the MotionDataProvider to get the information from the device sensors and use it to promote changes in the user interface.
struct ContentView: View {
// 1.
@StateObject private var motionManager = MotionDataProvider()
var body: some View {
VStack {
Text("Create with Swift")
.font(.system(size: 40).bold())
ZStack{
Circle()
.frame(width: 200)
Image(systemName: "swift")
.font(.system(size: 100).bold())
}
}
// 2.
.foregroundStyle(
.white.gradient.shadow(
.inner(color: .black, radius: 6, x: motionManager.x * -20, y: motionManager.y * -20)
)
)
// 4.
.rotation3DEffect(.degrees(motionManager.x * 20), axis: (x: 0, y: 1, z: 0))
.rotation3DEffect(.degrees(motionManager.y * 20), axis: (x: -1, y: 0, z: 0))
}
}
- The
motionManagerproperty is an instance of theMotionDataProviderand is constantly publishing the values read by theCMMotionManager. - The depth effect inside the view components will be achieved with the
shadowmodifier. The coordinate properties will receive the values from themotionManagerto calculate the shadow values. - Apply a rotation effect on the x and y axis of the view using the
rotation3DEffectmodifier. The calculation of the value of the rotation uses themotionManagerdata to define the amount ofdegreesto rotate the view.
Final Result
Once all the steps are complete, you can run the tests on a device, such as an iPhone. The expected result is that as you move the device, the shadows of the objects in the view will move to give us the sensation of depth, as shown in the example:
Here is the complete code:
import SwiftUI
import CoreMotion
class MotionDataProvider: ObservableObject {
private let motionProvider = CMMotionManager()
@Published var x = 0.0
@Published var y = 0.0
init() {
motionProvider.deviceMotionUpdateInterval = 1 / 20
motionProvider.startDeviceMotionUpdates(to: .main) { [weak self] data, error in
guard let motion = data?.attitude else { return }
self?.x = motion.roll
self?.y = motion.pitch
}
}
}
struct ContentView: View {
// 1.
@StateObject private var motionManager = MotionDataProvider()
var body: some View {
// 2.
VStack {
Text("Create with Swift")
.font(.system(size: 40).bold())
ZStack{
Circle().frame(width: 200) //.shadow(radius: 8)
Image(systemName: "swift")
.font(.system(size: 100).bold())
}
}
// 3.
.foregroundStyle(
.white.gradient.shadow(
.inner(color: .black, radius: 6, x: motionManager.x * -20, y: motionManager.y * -20)
)
)
// 4.
.rotation3DEffect(.degrees(motionManager.x * 20), axis: (x: 0, y: 1, z: 0))
.rotation3DEffect(.degrees(motionManager.y * 20), axis: (x: -1, y: 0, z: 0))
}
}
If you want to go deeper into the CoreMotion framework, how it works and what more you can do with it, check the following:
- CoreMotion Documentation: Apple documentation about the CoreMotion framework.
- CoreMotion WWDC23: What’s new in Core Motion at WWDC23.
- Using CoreMotion accelerometer: How to use Core Motion to read accelerometer data.