
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
motionProvider
constant is an instance ofCMMotionManager
, the object responsible for starting and managing motion services. - The properties
x
andy
will be updated by the motion manager object, they are tagged with the@Published
property wrapper. - When initializing the
MotionDataProvider
object, 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.main
parameter 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.
Thedata
anderror
values contain information about the device. The data and any possible errors found, respectively.
- Check if there is data to be read and access the
attitude
value, which contains the orientation of the device relative to a reference frame at a point in time, of the typeCMAttitude
. Theroll
value is assigned to thex
property, representing a rotation around a longitudinal axis. Thepitch
value is assigned to they
property, 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
motionManager
property is an instance of theMotionDataProvider
and is constantly publishing the values read by theCMMotionManager
. - The depth effect inside the view components will be achieved with the
shadow
modifier. The coordinate properties will receive the values from themotionManager
to calculate the shadow values. - Apply a rotation effect on the x and y axis of the view using the
rotation3DEffect
modifier. The calculation of the value of the rotation uses themotionManager
data to define the amount ofdegrees
to 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.