Treat this code as more of a guideline than a copy-paste example, because gyroscope-related code is a little difficult to test, but the core idea here is to keep track of elapsed time and rotation rate to determine how far the device has rotated.
var prevTime = DispatchTime.now()
var totalRotation: CGFloat = 0
var delta: CGFloat = 10
func startGyro() {
motionManager.gyroUpdateInterval = 0.2
motionManager.startGyroUpdates(to: OperationQueue.current!) { (data, Error) in
if let myData = data {
let currTime = DispatchTime.now()
let dt = 0
let interval = prevTime.distance(to: currTime)
if case .nanoseconds(let value) = interval {
dt = value
}
prevTime = currTime
// assuming rotationRate is in degrees/millisecond.
let dTheta: CGFloat = CGFloat(myData.rotationRate.y) * CGFloat(dt)
// If not milliseconds, convert dt to correct time unit
// If rotationRate is in radians, convert to degrees or make future comparison check for 2*pi instead of 360 degrees
totalRotation += dTheta
if totalRotation > 360 - delta && totalRotation < 360 + delta {
tiltOut = tiltOut + 1
self.labelText = "\(tiltOut)"
totalRotation -= 360
}
}
}
}
The delta is there because the startGyroUpdates
will not be called every time the device turns 1 degree, so you have to provide a buffer for determining what is okay for "about 360 degrees". Here I gave the range that if it has turned 350-370 degrees, it should increment the label by one. Of course, this uses discrete chunks of time, but assuming that the time interval between startGyroUpdates
calls and the change in rotationRate in that time span is relatively fine, there shouldn't be too much error. The code also makes the assumption that the phone will not rotate more than 360 degrees between startGyroUpdates
calls, which I think is a fair assumption.
If you know that the rotation rate will be fairly constant, you can also just use the average of the rotation rate and multiply that to the elapsed time.
Edit: Forgot let
before dTheta, and added code to convert DispatchTimeInterval
to usable Int
. You can add cases for seconds, milliseconds, etc., and convert dt
to the time unit for rotationRate
. See this SO link for more info on converting the result of prevTime.distance(to: currTime)
to an Int
that you can do math with. Make sure you don't lose precision when doing unit conversion.
Edit 2: Specified dTheta
to be of type CGFloat
Edit 3: Casted myData.rotationRate.y
and dt
to CGFloat