참고글: https://nshipster.com/cmdevicemotion/
눈을 내리도록 했는데, 눈이 유저가 기기를 돌려도 계속 중력의 방향으로 오게 하고 싶다면 어떻게 해야할까?
기기를 돌리는 각도만큼의 반대로 레이어를 돌려버리면 된다.
예를 들어
기기
- 똑바로 세우고 있다면 portrait
- 왼쪽으로 45도 눕혔다면
- 왼쪽으로 60도 눕혔다면
- 왼쪽으로 90도 눕혔다면 landscape
기기 각도
- 0도
- -45도
- -60도
- -90도
레이어 각도
- 0도
- 45도
- 60도
- 90도
근데 슬프게도 딱 기기 각도를 뱉어주는 API는 없다.
대신 CMMotionManager의 Accelerometer를 이용할 수 있다. 가속도를 나타내므로 속력, 운동방향, 시간을 포함하는 정보를 준다.
Accelerometer의 x, y를 이용하면 된다. atan2를 이용해서 x,y 좌표를 이용해 각도를 구할 수 있으니까!
여기서 양의 x축 기준으로 원하는 각도를 뱉어주는 x,y 좌표를 보면(마지막 열)
중간 열은 실제 x,y 값이다. https://nshipster.com/cmdevicemotion/ 참고하면 x, y +-가 되는 방향을 알 수 있다
- 똑바로 세우고 있다면 portrait
- 왼쪽으로 45도 눕혔다면
- 왼쪽으로 60도 눕혔다면
- 왼쪽으로 90도 눕혔다면 landscape
- (0, -1)
- (-루트2, -루트 2)
- (-루트2-a, -루트2+a)
- (-1,0)
- (1,0)
- (루트2, 루트2)
- (루트2-a, 루트2+a)
- (0,1)
실제 x,y에서 y,x로 바꾸고 양쪽에다 -를 곱해주면 된다.
atan2에 넣게되면 x, y는 y,x로 바뀌기 때문에 양쪽에 -만 곱해주면 된다.
func rotateSnowView() {
if motion.isAccelerometerAvailable {
motion.accelerometerUpdateInterval = 1.0 / 60.0
motion.startAccelerometerUpdates(to: .main) {
[weak self] (data, error) in
guard let data = data, error == nil else {
return
}
let rotation = atan2(-data.acceleration.x,
-data.acceleration.y)
self?.snowView.transform =
CGAffineTransform(rotationAngle: CGFloat(rotation))
}
}
}
근데 이렇게 했을 때 미세한 떨림이 나타난다.
회전하는 것 뿐만아니라 공간에서 기기를 움직여버리기 때문에, 회전 외의 값들이 가속도계에 영향을 주기 때문이다. 그럼 회전축에 관련된 값을 이용해보자. 회전축을 이용해서 Core Motion은 중력 작용의 가속도에서 유저의 움직임을 분리해준다. CMDeviceMotion 값을 이용하면 된다.
func rotateSnowView() {
if motion.isAccelerometerAvailable {
motion.accelerometerUpdateInterval = 1.0 / 60.0
motion.startAccelerometerUpdates(to: .main) {
[weak self] (data, error) in
guard let data = data, error == nil else {
return
}
let rotation = atan2(-data.acceleration.x,
-data.acceleration.y)
self?.snowView.transform =
CGAffineTransform(rotationAngle: CGFloat(rotation))
}
}
}