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

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



답변

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로 설정 한 경우에도 작동합니다.


답변

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

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


답변

마지막으로이 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
        }
    }
    


    답변

    첫째, 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 서브 클래스에 대해 그렇게 될 수 있는지 이야기 할 수 있습니까? 창문이 먹이 사슬의 상단에 있기 때문입니까?


    답변

    나는 “흔들기 (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)];


    답변

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

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


    답변

    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!")
        }
    }