iyOmSd/Title: Swift

[Swift] Core Motion (feat. 흔들기 감지센서 개발)

냄수 2023. 4. 19. 21:55
반응형

안녕하세요

이번에는 아이폰 흔들기를 감지하는 기능을 구현해볼려고합니다!

 

흔들기를 감지하는건 기본적으로 

UIResponder에 있는 메서드에서 쉽게 이벤트를 받아서 처리해볼 수 있어요

UIResponder니까 UIView, UIWindow, UIViewController... 어디에든 많이있죠

 

 

시점에따라서

began, ended가 있어서 원하는시점에 실행할수있고

취소했을때도 보이네요

 

이벤트가발생하면 UIEvent.EventSubtype이 전달되는데

폰을 흔들었을떄 motionShake이벤트를 받게되요

public override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    if motion == .motionShake {
        print("쉐킷")
    }
}

뷰컨트롤러 어딘가에서 이렇게정의만하면

흔들었을떄 해당 메서드가 실행되면서 정의해둔 동작을 실행시킬 수 있죠

 

하 지 만.

제가 생각했던 기대 동작은

폰을 1번흔들면 1번 흔든거고

연속으로 10번흔들면 10번이 흔들렸다를 체크해야하는 기능이였는데

이 메서드를 사용하게되면 기대하는 동작을 구현할 수 없었어요

 

motionBegan, motionEnded는

연속해서흔들면 멈추기전까지 계속 흔드는동안은 1번이라고 인식하게되는 한계가 있더라구여

흔들었을때 횟수가 중요치않은 QR띄우기같은 기능에는 적합해서 편하게 구현가능하지만

횟수가 중요한 기능에서는 적합치않았어요

 

그래서 찾아본게

 

오늘의주제 Core Motion 입니다!

Core Motion

기본적으로

CMMotionManager 객체를 이용해서 구현합니다

여러개존재할경우 센서값에 문제가 생길수 있기때문에 이객체는 1개만 존재해야합니다

 

CMMotionManager로 측정할 수 있는것들에는 아래와같이있어요

 

Gyroscope

핸드폰의 회전을 측정합니다

gyroUpdateInterval프로퍼티로 측정주기를 설정하고

마찬가지로 startGyroUpdates를 통해 시작하고 클로져로 콜백을 정의할 수 있습니다.

 

Magnetometer

자기계를 측정합니다

magnetometerUpdateInterval프로퍼티로 측정주기를 설정하고

startMagnetometerUpdates를 통해 시작하고 클로져로 콜백을 정의할 수 있습니다.

 

Device Motion

디바이스의 가속도, 회전, 중력, 자기력등 정보가 캡슐화된 타입입니다.

deviceMotionUpdateInterval프로퍼티로 측정주기를 설정하고

startDeviceMotionUpdates를 통해 시작하고 클로져로 콜백을 정의 할 수 있습니다.

 

Accelerometer

가속도측정

accelerometerUpdateInterval프로퍼티로 측정 주기를 설정할 수 있습니다.

startAccelerometerUpdates 메서드호출로 실행하고 클로져로 콜백을 정의합니다

let motionTimeInterval = 2
motionManager.accelerometerUpdateInterval = motionTimeInterval
print(motionManager.isAccelerometerAvailable)

// 업데이트 주기마다 콜백으로 클로져실행
motionManager.startAccelerometerUpdates(to: .main) { data, error in
    guard let x = data?.acceleration.x,
          let y = data?.acceleration.y,
          let z = data?.acceleration.z else {
        return
    }
    print("x: ", x)
    print("y: ", y)
    print("z: ", z)
}

저는 흔들기쪽에 관심이있어서 흔들기쪽 코드만 첨부하고.. 나머지는 간단하게 개념만 언급해봤어요!

 

이렇게 찍으면 흔들때마다 x, y, z로 얼만큼의 가속도인지 값이 측정주기마다 출력되요

 

핸드폰을 가만히 세워두면 기본적으로 1근사값 정도의 센서값이 찍힐거에요

이값은

중력가속도가 적용된값이라고 생각하시면 됩니다!

중력가속도는 9.8m/s^2 인데 이를 1로 나타낸다고해요

 

x좌표로든 y좌표로든 z좌표로든 흔들면 흔든거기때문에 측정을해야해서

공식으로

let speed = sqrt(pow(x,2) + pow(y,2) + pow(z,2))

찾아보니 이런 공식이 있더라... 하더라구여?

물리는 잘몰라서... 네 그렇답니다...

이 speed가 일정수치 이상일때 흔든걸로 체크를 

살짝만 흔들어도 speed값으로 1~2이 떠서 정말흔듦을 측정하기 애매한 스피드라

계속된 테스트를 통해서 원하는 세기에 맞는 speed값을 정의해서 사용하는방향으로 구현하는게 최선이라고 생각했어요

 

아래는 측정스피드를 화면에띄우고 카운팅하는 로직까지포함된 코드입니다

motionManager.accelerometerUpdateInterval = motionTimeInterval
print(motionManager.isAccelerometerAvailable)
motionManager.startAccelerometerUpdates(to: .main) { data, error in
    guard let x = data?.acceleration.x,
          let y = data?.acceleration.y,
          let z = data?.acceleration.z else {
        return
    }
    print("x: ", x)
    print("y: ", y)
    print("z: ", z)
    let speed = sqrt(pow(x,2)) + sqrt(pow(y,2)) + sqrt(pow(z,2))
    
    if speed > accelerationLimit {
        currentSpeed = speed
        count += 1
        print("✅✅✅✅✅✅✅")
    }
    
    print(speed)
}

여기서 생기는 이슈로

계속흔들고있을때 

정해진 speed 를 넘어간상태라면 주기마다 계속카운팅되는 이슈가 있어요

즉, 1번흔들었지만 그사이에 측정주기가 3번흘렀고, speed도 흔듦을 만족한상태라면 +1이아닌 +1+1+1이되는 현상이 있기때문에 수치가 3이올라가서 부자연스러운 이슈가있었어요..

이부분은 테스트를 계속해보면서 자연스러운 주기를 찾아야하는게 최선인것같아요 🥲

 

열심히흔든 영상입니다.. ㅎㅎ

 

 

Core Motion으로 아이폰흔들기를 감지하는 재밋는 기능을 개발중입니다 :)

 

 

https://developer.apple.com/documentation/coremotion/getting_processed_device-motion_data

 

Getting Processed Device-Motion Data | Apple Developer Documentation

Retrieve data that has already been processed to remove environmental bias, such as the effects of gravity.

developer.apple.com

 

반응형