누군가가 iPhone을 흔들면 어떻게 감지합니까?


340

누군가가 iPhone을 흔들면 반응하고 싶습니다. 나는 그들이 어떻게 그것을 흔들리는 지에 대해 신경 쓰지 않고 단지 순간적으로 격렬하게 흔들렸다. 누구든지 이것을 감지하는 방법을 알고 있습니까?

답변:


292

3.0에서는 이제 더 쉬운 방법이 있습니다. 새로운 모션 이벤트에 연결하십시오.

주요 요령은 흔들림 이벤트 메시지를 수신하기 위해 firstResponder로 원하는 일부 UIView (UIViewController가 아님)가 있어야한다는 것입니다. 모든 UIView에서 흔들림 이벤트를 얻을 수있는 코드는 다음과 같습니다.

@implementation ShakingView

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if ( event.subtype == UIEventSubtypeMotionShake )
    {
        // Put in code here to handle shake
    }

    if ( [super respondsToSelector:@selector(motionEnded:withEvent:)] )
        [super motionEnded:motion withEvent:event];
}

- (BOOL)canBecomeFirstResponder
{ return YES; }

@end

UIView (시스템 뷰조차 포함)를 이러한 메소드만으로 뷰를 서브 클래 싱 한 다음 IB에서 기본 유형 대신이 새로운 유형을 선택하거나 또는 전망).

뷰 컨트롤러에서이 뷰를 첫 번째 응답자가되도록 설정하려고합니다.

- (void) viewWillAppear:(BOOL)animated
{
    [shakeView becomeFirstResponder];
    [super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
    [shakeView resignFirstResponder];
    [super viewWillDisappear:animated];
}

검색 창이나 텍스트 입력 필드와 같이 사용자 작업에서 첫 번째 응답자가되는 다른보기가있는 경우 다른보기가 사임 할 때 흔들리는보기 첫 번째 응답자 상태도 복원해야합니다.

이 메소드는 applicationSupportsShakeToEdit를 NO로 설정 한 경우에도 작동합니다.


5
이것은 나를 위해 작동하지 않았습니다 : viewDidAppear대신 컨트롤러를 재정의해야했습니다 viewWillAppear. 왜 그런지 잘 모르겠습니다. 어쩌면 흔들림 이벤트 수신을 시작하기 위해 무엇이든 할 수 있기 전에보기를 볼 수 있어야합니까?
Kristopher Johnson

1
이것은 쉽지만 반드시 좋은 것은 아닙니다. 길고 연속적인 흔들림을 감지 motionEnded하려면 흔들림이 실제로 멈추기 전에 iPhone이 발사 하기를 좋아하므로이 방법은 유용하지 않습니다 . 따라서이 방법을 사용하면 하나의 긴 흔들림이 아닌 분리 된 일련의 짧은 흔들림이 발생합니다. 이 경우 다른 대답이 훨씬 잘 작동합니다.
aroth

3
@Kendall-UIViewControllers는 UIResponder를 구현하고 응답자 체인에 있습니다. 최상위 UIWindow도 있습니다. developer.apple.com/library/ios/DOCUMENTATION/EventHandling/…
DougW

1
[super respondsToSelector:를 호출 [self respondsToSelector:하는 것과 동일하므로 원하는 것을 수행하지 않습니다 YES. 필요한 것은 [[ShakingView superclass] instancesRespondToSelector:입니다.
Vadim Yelagin

2
if (event.subtype == UIEventSubtypeMotionShake) 대신 다음을 사용할 수 있습니다. if (motion == UIEventSubtypeMotionShake)
Hashim Akhtar

179

Diceshaker 응용 프로그램에서 :

// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
    double
        deltaX = fabs(last.x - current.x),
        deltaY = fabs(last.y - current.y),
        deltaZ = fabs(last.z - current.z);

    return
        (deltaX > threshold && deltaY > threshold) ||
        (deltaX > threshold && deltaZ > threshold) ||
        (deltaY > threshold && deltaZ > threshold);
}

@interface L0AppDelegate : NSObject <UIApplicationDelegate> {
    BOOL histeresisExcited;
    UIAcceleration* lastAcceleration;
}

@property(retain) UIAcceleration* lastAcceleration;

@end

@implementation L0AppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [UIAccelerometer sharedAccelerometer].delegate = self;
}

- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {

    if (self.lastAcceleration) {
        if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
            histeresisExcited = YES;

            /* SHAKE DETECTED. DO HERE WHAT YOU WANT. */

        } else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
            histeresisExcited = NO;
        }
    }

    self.lastAcceleration = acceleration;
}

// and proper @synthesize and -dealloc boilerplate code

@end

히스테리시스는 사용자가 흔들림을 멈출 때까지 흔들림 이벤트가 여러 번 트리거되는 것을 방지합니다.


7
참고 3.0에 대한 더 쉬운 방법을 제시하는 아래 답변을 추가했습니다.
Kendall Helmstetter Gelner

2
특정 축에서 정확하게 흔들면 어떻게됩니까?
jprete

5
iOS 3.0 motionBeganmotionEnded이벤트가 흔들림의 정확한 시작과 끝을 감지하는 관점에서 정확하지 않거나 정확하지 않기 때문에 가장 좋은 대답 입니다. 이 방법을 사용하면 원하는만큼 정확할 수 있습니다.
aroth

2
UIAccelerometerDelegate 가속도계 : didAccelerate : 는 iOS5에서 더 이상 사용되지 않습니다.
Yantao Xie

이 다른 대답이 구현하는 더 빠른 stackoverflow.com/a/2405692/396133
Abramodj

154

마지막으로이 Undo / Redo Manager Tutorial의 코드 예제를 사용하여 작동하게했습니다 .
이것은 당신이해야 할 일입니다.

  • 앱의 델리게이트에서 applicationSupportsShakeToEdit 속성을 설정하십시오 .
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • View Controller의 canBecomeFirstResponder , viewDidAppear :viewWillDisappear : 메소드 추가 / 재정의 :
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • motionEnded 메소드를 View Controller에 추가하십시오 .
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    

    Eran Talmor 솔루션에 추가하려면 새 프로젝트 선택기에서 이러한 응용 프로그램을 선택한 경우 뷰 컨트롤러 대신 내비게이션 컨트롤러를 사용해야합니다.
    Rob

    나는 이것을 사용해 보았지만 때로는 떨림이나 가벼운 흔들림이 멈추었다
    vinothp

    1 단계의 기본 값으로, 현재 중복 applicationSupportsShakeToEditIS YES.
    DonVaughn

    1
    [self resignFirstResponder];앞서 전화 [super viewWillDisappear:animated];해야합니까? 특이한 것 같습니다.
    JaredH

    94

    첫째, Kendall의 7 월 10 일 답변은 현장에 있습니다.

    이제 ... 나는 비슷한 일을하고 싶었습니다 (iPhone OS 3.0 이상). 제 경우에만 앱 전체에서 원했기 때문에 흔들림이 발생했을 때 앱의 다양한 부분을 경고 할 수있었습니다 . 여기 내가 한 일이 있습니다.

    먼저 UIWindow를 서브 클래 싱했습니다 . 쉬워요. 다음과 같은 인터페이스를 사용하여 새 클래스 파일을 만듭니다 MotionWindow : UIWindow(자유롭게 선택하십시오). 다음과 같은 방법을 추가하십시오.

    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self];
        }
    }
    

    @"DeviceShaken"선택한 알림 이름으로 변경하십시오 . 파일을 저장하십시오.

    이제 MainWindow.xib (스톡 Xcode 템플릿)를 사용하는 경우 거기에 들어가서 Window 객체의 클래스를 UIWindow 에서 MotionWindow 또는 원하는 이름으로 변경하십시오. xib를 저장하십시오. 프로그래밍 방식으로 UIWindow 를 설정하는 경우 대신 새 Window 클래스를 사용하십시오.

    이제 앱에서 특수한 UIWindow 클래스를 사용하고 있습니다. 흔들림에 대해 듣고 싶은 곳이라면 어디든지 알림을 신청하십시오! 이처럼 :

    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
    

    관찰자로 자신을 제거하려면 :

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    viewWillAppear :viewWillDisappear : View Controller가 관련된 곳에 넣었습니다 . 흔들림 이벤트에 대한 응답이 "이미 진행 중"인지 아닌지 확인하십시오. 그렇지 않으면 장치가 연속해서 두 번 흔들리면 교통 체증이 발생할 수 있습니다. 이렇게하면 원래 알림에 실제로 응답 할 때까지 다른 알림을 무시할 수 있습니다.

    또한 : 당신은 motionBeganmotionEnded 큐를 선택할 수 있습니다 . 그것은 당신에게 달려 있습니다. 필자의 경우 장치가 정지 한 후에 ( 효과 가 흔들 리기 시작할 때) 효과가 항상 발생해야 하므로 motionEnded 사용 합니다. 둘 다 시도하고 어느 것이 더 의미가 있는지 확인하십시오 ... 또는 둘 다 감지 / 알림하십시오!

    여기에 한 가지 더 궁금한 점이 있습니다.이 코드에서 첫 번째 응답자 관리의 징후가 없음을 주목하십시오. 나는 지금까지 테이블 뷰 컨트롤러로 이것을 시도했지만 모든 것이 아주 잘 작동하는 것 같습니다! 그래도 다른 시나리오를 보증 할 수는 없습니다.

    켄달 등 al- 왜 이것이 UIWindow 서브 클래스에 대해 그렇게 될 수 있는지 이야기 할 수 있습니까? 창문이 먹이 사슬의 상단에 있기 때문입니까?


    1
    너무 이상합니다. 그대로 작동합니다. 잘만되면 그것은 "자기 일에도 불구하고 일하는"경우가 아닙니다! 테스트에서 찾은 내용을 알려주십시오.
    Joe D' Andrea

    나는 이것이 정규 UIView를 서브 클래스 화하는 것을 선호합니다. 특히 하나의 위치에만 존재하기 때문에 창 서브 클래스를 넣는 것은 자연스러운 것처럼 보입니다.
    Shaggy Frog

    jdandrea 감사합니다, 나는 당신의 방법을 사용하지만 여러 번 떨고 있습니다! 나는 단지 사용자가 한 번만 흔들어주기를 원한다. 어떻게 처리 할 수 ​​있습니까? 나는 이와 같은 sth를 구현한다. 나는 다음과 같이 내 코드를 구현한다 : i-phone.ir/forums/uploadedpictures/… ---- 문제는 응용 프로그램의 수명 동안 응용 프로그램이 한 번만 흔들리는 것입니다! 볼뿐만 아니라
    Momi

    1
    Momeks : 장치가 정지 된 후에 (포스트 후) 알림이 발생해야하는 경우 motionBegan 대신 motionEnded를 사용하십시오. 그렇게해야합니다!
    Joe D' Andrea 1

    1
    @Joe D' Andrea-UIWindow가 응답자 체인에 있기 때문에 그대로 작동합니다. 높은 체인까지 무언가가 가로 채서 이러한 이벤트를 소비하는 경우, 그것은 것입니다 하지 를받을 수 있습니다. developer.apple.com/library/ios/DOCUMENTATION/EventHandling/…
    DougW

    34

    나는 "흔들기 (shaking)"구현을 찾기 위해이 포스트를 발견했다. millenomi의 대답은 나를 위해 잘 작동했지만 트리거하기 위해 조금 더 "떨리는 행동"이 필요한 것을 찾고있었습니다. 부울 값을 int shakeCount로 바꿨습니다. 또한 Objective-C에서 L0AccelerationIsShaking () 메서드를 다시 구현했습니다. shakeCount에 추가 된 금액을 조정하여 필요한 흔들림 량을 조정할 수 있습니다. 아직 최적의 값을 찾았는지 확실하지 않지만 지금까지는 잘 작동하는 것 같습니다. 이것이 누군가를 돕기를 바랍니다.

    - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
        if (self.lastAcceleration) {
            if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7] && shakeCount >= 9) {
                //Shaking here, DO stuff.
                shakeCount = 0;
            } else if ([self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.7]) {
                shakeCount = shakeCount + 5;
            }else if (![self AccelerationIsShakingLast:self.lastAcceleration current:acceleration threshold:0.2]) {
                if (shakeCount > 0) {
                    shakeCount--;
                }
            }
        }
        self.lastAcceleration = acceleration;
    }
    
    - (BOOL) AccelerationIsShakingLast:(UIAcceleration *)last current:(UIAcceleration *)current threshold:(double)threshold {
        double
        deltaX = fabs(last.x - current.x),
        deltaY = fabs(last.y - current.y),
        deltaZ = fabs(last.z - current.z);
    
        return
        (deltaX > threshold && deltaY > threshold) ||
        (deltaX > threshold && deltaZ > threshold) ||
        (deltaY > threshold && deltaZ > threshold);
    }
    

    추신 : 업데이트 간격을 1/15 초로 설정했습니다.

    [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];

    파노라마처럼 장치의 움직임을 어떻게 감지합니까?
    user2526811

    iPhone이 X 방향으로 만 움직일 때 흔들림을 식별하는 방법은 무엇입니까? Y 또는 Z 방향 인 경우 흔들림을 감지하고 싶지 않습니다.
    Manthan

    12

    UIAccelerometerDelegate 프로토콜의 일부인 accelerometer : didAccelerate : 메소드를 통해 가속도계를 확인하고 값이 흔들림에 필요한 이동량의 임계 값을 초과하는지 확인해야합니다.

    iPhone 개발자 사이트에서 사용 가능한 GLPaint 예제의 AppController.m 하단에 가속도계 : didAccelerate : 메소드에 적절한 샘플 코드가 있습니다.


    12

    Swift를 사용하는 iOS 8.3 (아마도)에서는 뷰 컨트롤러에서 motionBegan또는 motionEnded메서드 를 재정의하는 것만 큼 간단 합니다.

    class ViewController: UIViewController {
        override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent) {
            println("started shaking!")
        }
    
        override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
            println("ended shaking!")
        }
    }

    해봤 어? 앱이 백그라운드에있을 때이 기능을 사용하려면 약간의 추가 노력이 필요하다고 가정합니다.
    nhgrif

    그렇습니다. 곧 .. 그러나 iPhone 6s를 발견 한 경우이 "픽업 깨우기 화면"기능이 있습니다.이 기능은 장비를 집어 들었을 때 화면이 한동안 밝아집니다. 이 동작을 포착해야합니다
    nr5

    9

    이것은 기본 위임 코드입니다.

    #define kAccelerationThreshold      2.2
    
    #pragma mark -
    #pragma mark UIAccelerometerDelegate Methods
        - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration 
        {   
            if (fabsf(acceleration.x) > kAccelerationThreshold || fabsf(acceleration.y) > kAccelerationThreshold || fabsf(acceleration.z) > kAccelerationThreshold) 
                [self myShakeMethodGoesHere];   
        }

    또한 인터페이스에서 적절한 코드를 설정하십시오. 즉 :

    @interface MyViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>


    파노라마처럼 장치의 움직임을 어떻게 감지합니까?
    user2526811

    iPhone이 X 방향으로 만 움직일 때 흔들림을 식별하는 방법은 무엇입니까? Y 또는 Z 방향 인 경우 흔들림을 감지하고 싶지 않습니다.
    Manthan


    7

    ViewController.m 파일에 다음 메소드를 추가하면 제대로 작동합니다.

        -(BOOL) canBecomeFirstResponder
        {
             /* Here, We want our view (not viewcontroller) as first responder 
             to receive shake event message  */
    
             return YES;
        }
    
        -(void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
        {
                if(event.subtype==UIEventSubtypeMotionShake)
                {
                        // Code at shake event
    
                        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Motion" message:@"Phone Vibrate"delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
                        [alert show];
                        [alert release];
    
                        [self.view setBackgroundColor:[UIColor redColor]];
                 }
        }
        - (void)viewDidAppear:(BOOL)animated
        {
                 [super viewDidAppear:animated];
                 [self becomeFirstResponder];  // View as first responder 
         }

    5

    이것을 의견이 아닌 답변으로 게시하는 것은 죄송하지만 알 수 있듯이 스택 오버플로가 처음이므로 의견을 올릴만큼 평판이 좋지 않습니다!

    어쨌든 뷰가 뷰 계층의 일부가되면 첫 번째 응답자 상태를 설정하는 것에 대해 두 번째 @cire입니다. viewDidLoad예를 들어 뷰 컨트롤러 방법 에서 첫 번째 응답자 상태를 설정 하면 작동하지 않습니다. 그리고 그것이 작동하는지 확실하지 않으면 [view becomeFirstResponder]테스트 할 수있는 부울을 반환합니다.

    또 다른 요점 : UIView 서브 클래스를 불필요하게 생성하지 않으려면 뷰 컨트롤러를 사용하여 흔들림 이벤트를 캡처 할 있습니다. 그다지 번거롭지는 않지만 여전히 옵션이 있다는 것을 알고 있습니다. Kendall이 UIView 서브 클래스에 넣은 코드 스 니펫을 컨트롤러로 옮기고 UIView 서브 클래스 대신 becomeFirstResponderresignFirstResponder메시지를 보내기 만하면 self됩니다.


    이것은 질문에 대한 답변을 제공하지 않습니다. 저자에게 비평을하거나 설명을 요청하려면 게시물 아래에 의견을 남겨주십시오.
    Alex Salauyou

    @SashaSalauyou 나는 여기에 첫 번째 문장에서 명확하게 언급했다 당시에 의견을 게시하기에 충분한 평판이 없었습니다
    Newtz

    죄송합니다. 여기에서 5-yo 답변을 검토 대기열에 넣을 수 있습니다))
    Alex Salauyou

    5

    우선, 나는 이것이 오래된 게시물이라는 것을 알고 있지만 여전히 관련이 있으며, 가장 높은 투표 응답 두 개가 가능한 한 빨리 흔들림을 감지 하지 못한다는 것을 알았습니다 . 방법은 다음과 같습니다.

    1. 대상의 빌드 단계에서 CoreMotion을 프로젝트에 연결하십시오. CoreMotion
    2. ViewController에서 :

      - (BOOL)canBecomeFirstResponder {
          return YES;
      }
      
      - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
      {
          if (motion == UIEventSubtypeMotionShake) {
              // Shake detected.
          }
      }

    4

    가장 쉬운 해결책은 응용 프로그램에 대한 새로운 루트 창을 파생시키는 것입니다.

    @implementation OMGWindow : UIWindow
    
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
        if (event.type == UIEventTypeMotion && motion == UIEventSubtypeMotionShake) {
            // via notification or something   
        }
    }
    @end

    그런 다음 응용 프로그램 위임에서 :

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[OMGWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        //…
    }

    스토리 보드를 사용하는 경우 까다로울 수 있습니다. 응용 프로그램 대리자에 필요한 코드를 정확하게 모르겠습니다.


    그리고 Xcode 5부터 기본 클래스를 파생 시킬 수 있습니다 @implementation.
    mxcl

    이것은 작동하며 여러 앱에서 사용합니다. 따라서 왜 투표에 실패했는지 확실하지 않습니다.
    mxcl

    매력처럼 일했다. 궁금한 경우 다음과 같이 창 클래스를 재정의 할 수 있습니다. stackoverflow.com/a/10580083/709975
    Edu

    앱이 백그라운드에있을 때 작동합니까? 다른 아이디어가 없다면 백그라운드에서 감지하는 방법?
    nr5

    배경 모드가 설정되어 있으면 아마도 작동합니다.
    mxcl

    1

    이 세 가지 방법을 사용하십시오.

    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{

    자세한 내용은 전체 예제 코드를 확인할 수 있습니다.


    1

    첫 번째 답변을 기반으로 한 swiftease 버전!

    override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
        if ( event?.subtype == .motionShake )
        {
            print("stop shaking me!")
        }
    }

    -1

    이 응용 프로그램 전체에서 사용할 수 있도록 UIWindow에 범주를 만들었습니다.

    @implementation UIWindow (Utils)
    
    - (BOOL)canBecomeFirstResponder
    {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Do whatever you want here...
        }
    }
    
    @end
    당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
    Licensed under cc by-sa 3.0 with attribution required.