답변:
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로 설정 한 경우에도 작동합니다.
motionEnded
하려면 흔들림이 실제로 멈추기 전에 iPhone이 발사 하기를 좋아하므로이 방법은 유용하지 않습니다 . 따라서이 방법을 사용하면 하나의 긴 흔들림이 아닌 분리 된 일련의 짧은 흔들림이 발생합니다. 이 경우 다른 대답이 훨씬 잘 작동합니다.
[super respondsToSelector:
를 호출 [self respondsToSelector:
하는 것과 동일하므로 원하는 것을 수행하지 않습니다 YES
. 필요한 것은 [[ShakingView superclass] instancesRespondToSelector:
입니다.
내 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
히스테리시스는 사용자가 흔들림을 멈출 때까지 흔들림 이벤트가 여러 번 트리거되는 것을 방지합니다.
motionBegan
과 motionEnded
이벤트가 흔들림의 정확한 시작과 끝을 감지하는 관점에서 정확하지 않거나 정확하지 않기 때문에 가장 좋은 대답 입니다. 이 방법을 사용하면 원하는만큼 정확할 수 있습니다.
마지막으로이 Undo / Redo Manager Tutorial의 코드 예제를 사용하여 작동하게했습니다 .
이것은 당신이해야 할 일입니다.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.applicationSupportsShakeToEdit = YES;
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
// your code
}
}
applicationSupportsShakeToEdit
IS YES
.
[self resignFirstResponder];
앞서 전화 [super viewWillDisappear:animated];
해야합니까? 특이한 것 같습니다.
첫째, 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가 관련된 곳에 넣었습니다 . 흔들림 이벤트에 대한 응답이 "이미 진행 중"인지 아닌지 확인하십시오. 그렇지 않으면 장치가 연속해서 두 번 흔들리면 교통 체증이 발생할 수 있습니다. 이렇게하면 원래 알림에 실제로 응답 할 때까지 다른 알림을 무시할 수 있습니다.
또한 : 당신은 motionBegan 대 motionEnded 큐를 선택할 수 있습니다 . 그것은 당신에게 달려 있습니다. 필자의 경우 장치가 정지 한 후에 ( 효과 가 흔들 리기 시작할 때) 효과가 항상 발생해야 하므로 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)];
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!")
}
}
이것은 기본 위임 코드입니다.
#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>
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
}
이것을 의견이 아닌 답변으로 게시하는 것은 죄송하지만 알 수 있듯이 스택 오버플로가 처음이므로 의견을 올릴만큼 평판이 좋지 않습니다!
어쨌든 뷰가 뷰 계층의 일부가되면 첫 번째 응답자 상태를 설정하는 것에 대해 두 번째 @cire입니다. viewDidLoad
예를 들어 뷰 컨트롤러 방법 에서 첫 번째 응답자 상태를 설정 하면 작동하지 않습니다. 그리고 그것이 작동하는지 확실하지 않으면 [view becomeFirstResponder]
테스트 할 수있는 부울을 반환합니다.
또 다른 요점 : UIView 서브 클래스를 불필요하게 생성하지 않으려면 뷰 컨트롤러를 사용하여 흔들림 이벤트를 캡처 할 수 있습니다. 그다지 번거롭지는 않지만 여전히 옵션이 있다는 것을 알고 있습니다. Kendall이 UIView 서브 클래스에 넣은 코드 스 니펫을 컨트롤러로 옮기고 UIView 서브 클래스 대신 becomeFirstResponder
및 resignFirstResponder
메시지를 보내기 만하면 self
됩니다.
우선, 나는 이것이 오래된 게시물이라는 것을 알고 있지만 여전히 관련이 있으며, 가장 높은 투표 응답 두 개가 가능한 한 빨리 흔들림을 감지 하지 못한다는 것을 알았습니다 . 방법은 다음과 같습니다.
ViewController에서 :
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake) {
// Shake detected.
}
}
가장 쉬운 해결책은 응용 프로그램에 대한 새로운 루트 창을 파생시키는 것입니다.
@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];
//…
}
스토리 보드를 사용하는 경우 까다로울 수 있습니다. 응용 프로그램 대리자에 필요한 코드를 정확하게 모르겠습니다.
@implementation
.
첫 번째 답변을 기반으로 한 swiftease 버전!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if ( event?.subtype == .motionShake )
{
print("stop shaking me!")
}
}
viewDidAppear
대신 컨트롤러를 재정의해야했습니다viewWillAppear
. 왜 그런지 잘 모르겠습니다. 어쩌면 흔들림 이벤트 수신을 시작하기 위해 무엇이든 할 수 있기 전에보기를 볼 수 있어야합니까?