当有人摇晃iPhone时,我想做出反应。我并不特别在意他们是如何摇晃它的,只在意它在一瞬间被使劲地挥舞着。有人知道怎么检测吗?
查看GLPaint示例。
http://developer.apple.com/library/ios/#samplecode/GLPaint/Introduction/Intro.html
你需要通过accelerometer:didAccelerate: method检查加速度计,这是UIAccelerometerDelegate协议的一部分,检查这些值是否超过了震动所需的移动量的阈值。
在AppController底部的accelerometer:didAccelerate:方法中有一些不错的示例代码。m在GLPaint例子中,这个例子可以在iPhone开发者网站上找到。
从我的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
组织性可以防止震动事件多次触发,直到用户停止震动。
我偶然发现这篇文章是为了寻找一个“摇动”的实现。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); }
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
这是你需要的基本委托代码:
#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]; }
也在接口中的适当代码中设置。即:
UIPickerViewDelegate, UIPickerViewDataSource, UIAccelerometerDelegate>
在3.0,现在有一个更简单的方法-挂钩到新的运动事件。
主要的技巧是你需要有一些UIView(不是UIViewController)你想要作为firstResponder来接收震动事件消息。这是你可以在任何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,此方法也有效。
抱歉把这篇文章作为一个答案而不是评论,但正如你所看到的,我是Stack Overflow的新手,所以我还没有足够的声誉来发表评论!
不管怎样,我第二个@cire关于确保设置第一响应器状态一旦视图是视图层次结构的一部分。例如,在视图控制器viewDidLoad方法中设置第一响应器状态将不起作用。如果你不确定它是否工作,[view becomeFirstResponder]返回一个你可以测试的布尔值。
viewDidLoad
[view becomeFirstResponder]
另一点:如果你不想创建一个不必要的UIView子类,你可以使用一个视图控制器来捕获抖动事件。我知道没有那么麻烦,但还是有选择的。只需将Kendall放入UIView子类的代码片段移动到控制器中,并将becomeFirstResponder和resignFirstResponder消息发送到self而不是UIView子类。
becomeFirstResponder
resignFirstResponder
self
首先,肯德尔7月10日的回答是正确的。
现在…我想做一些类似的事情(在iPhone OS 3.0+),只是在我的情况下,我想它应用程序范围内,这样我可以提醒各种各样的部分的应用程序时,震动发生。这是我最后做的事情。
首先,我子类化了ui窗口。这很容易。创建一个带有MotionWindow : UIWindow接口的新类文件(当然,你可以自己选择)。添加一个这样的方法:
MotionWindow : UIWindow
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { [[NSNotificationCenter defaultCenter] postNotificationName:@"DeviceShaken" object:self]; } }
将@"DeviceShaken"更改为您选择的通知名称。保存文件。
@"DeviceShaken"
现在,如果你使用主窗口。xib (Xcode模板中常用的东西),然后把Window对象的类从ui窗口改为MotionWindow或者其他你叫它的东西。保存xib。如果你以编程方式设置ui窗口,则使用你的新Window类。
现在你的应用程序正在使用专门的ui窗口类。无论你想在哪里被告知摇晃,注册他们的通知!是这样的:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceShaken) name:@"DeviceShaken" object:nil];
把自己从观察者的身份移开:
[[NSNotificationCenter defaultCenter] removeObserver:self];
我把我的放在那些:和viewWillDisappear:中,其中涉及到视图控制器。确保您对震动事件的响应知道它是否“已经在进行中”。否则,如果设备连续摇晃两次,你就会遇到小塞车。通过这种方式,您可以忽略其他通知,直到真正完成对原始通知的响应。
另外:你可以选择motionBegan和motionEnded的cue off。由你决定。在我的情况下,效果总是需要发生后设备处于休息状态(而不是当它开始摇晃时),所以我使用motionEnded。两个都试一下,看看哪个更有意义……或者检测/通知两者!
这里还有一个(奇怪的?)观察:注意,这段代码中没有第一响应器管理的迹象。到目前为止,我只尝试了表视图控制器,一切似乎都很好地工作在一起!但我不能保证其他情况。
Kendall,等等——谁能解释一下为什么ui窗口子类可能如此?是因为窗户位于食物链的顶端吗?
我最终使它工作使用的代码示例从这个撤消/重做管理器教程. 这正是你需要做的:
- (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 } }
在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 }
只要用这三种方法就可以了
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{ - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event{ - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{
有关详细信息,您可以查看在那里上的完整示例代码
最简单的解决方案是为你的应用程序派生一个新的根窗口:
@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]; //… }
如果你使用的是故事板,这可能会更棘手,我不知道你在应用委托中需要精确的代码。
首先,我知道这是一个老帖子,但它仍然是相关的,我发现两个投票最高的答案没有尽早发现震动。下面是如何做到这一点:
在你的ViewController:
- (BOOL)canBecomeFirstResponder { return YES; } - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // Shake detected. } }
在iOS 8.3(也许更早)的Swift中,重写视图控制器中的motionBegan或motionEnded方法非常简单:
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!") } }
一个基于第一个答案的swif挑逗版本!
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { if ( event?.subtype == .motionShake ) { print("stop shaking me!") } }
为了在整个应用范围内启用这个功能,我在UIWindow上创建了一个类别:
@implementation UIWindow (Utils) - (BOOL)canBecomeFirstResponder { return YES; } - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { // Do whatever you want here... } } @end
在swift 5中,这就是如何捕捉运动并进行检查
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { if motion == .motionShake { print("shaking") } }