如何检测有人摇晃 iPhone?

发布于 2024-07-06 01:03:00 字数 69 浏览 10 评论 0原文

我想在有人摇晃 iPhone 时做出反应。 我并不特别关心他们如何摇动它,只是在瞬间用力挥动它。 有谁知道如何检测这个?

I want to react when somebody shakes the iPhone. I don't particularly care how they shake it, just that it was waved vigorously about for a split second. Does anyone know how to detect this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

压抑⊿情绪 2024-07-13 01:03:01

为了在应用程序范围内启用此功能,我在 UIWindow 上创建了一个类别:

@implementation UIWindow (Utils)

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake) {
        // Do whatever you want here...
    }
}

@end

To enable this app-wide, I created a category on UIWindow:

@implementation UIWindow (Utils)

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake) {
        // Do whatever you want here...
    }
}

@end
梦纸 2024-07-13 01:03:01

在 swift 5 中,您可以这样捕捉运动并检查

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

In swift 5, this is how you can capture motion and check

override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
   if motion == .motionShake 
   {
      print("shaking")
   }
}
生死何惧 2024-07-13 01:03:01

最简单的解决方案是为您的应用程序派生一个新的根窗口:

@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];
    //…
}

如果您使用的是故事板,这可能会更棘手,我不知道您在应用程序委托中确切需要的代码。

Easiest solution is to derive a new root window for your application:

@implementation OMGWindow : UIWindow

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

Then in your application delegate:

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

If you are using a Storyboard, this may be trickier, I don’t know the code you will need in the application delegate precisely.

情痴 2024-07-13 01:03:01

只需使用这三种方法即可完成

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

详细信息,您可以通过 那里

Just use these three methods to do it

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

for details you may check a complete example code over there

秋心╮凉 2024-07-13 01:03:01

基于第一个答案的 swiftease 版本!

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

A swiftease version based on the very first answer!

override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
    if ( event?.subtype == .motionShake )
    {
        print("stop shaking me!")
    }
}
悲凉≈ 2024-07-13 01:03:01

在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 
     }

Add Following methods in ViewController.m file, its working properly

    -(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 
     }
私藏温柔 2024-07-13 01:03:01

这是您需要的基本委托代码:

#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

This is the basic delegate code you need:

#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];   
    }

Also set the in the appropriate code in the Interface. i.e:

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

强辩 2024-07-13 01:03:01

您需要通过 accelerometer:didAccelerate: 方法(UIAccelerometerDelegate 协议的一部分)检查加速度计,并检查这些值是否超过摇动所需移动量的阈值。

GLPaint 示例中 AppController.m 底部的 Accelerometer:didAccelerate: 方法中有不错的示例代码,该示例可在 iPhone 开发人员网站上找到。

You need to check the accelerometer via accelerometer:didAccelerate: method which is part of the UIAccelerometerDelegate protocol and check whether the values go over a threshold for the amount of movement needed for a shake.

There is decent sample code in the accelerometer:didAccelerate: method right at the bottom of AppController.m in the GLPaint example which is available on the iPhone developer site.

蘑菇王子 2024-07-13 01:03:01

在使用 Swift 的 iOS 8.3(可能更早)中,只需重写视图控制器中的 motionBeganmotionEnded 方法即可:

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

In iOS 8.3 (perhaps earlier) with Swift, it's as simple as overriding the motionBegan or motionEnded methods in your view controller:

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!")
    }
}
潦草背影 2024-07-13 01:03:01

首先,我知道这是一篇旧帖子,但它仍然相关,而且我发现得票最高的两个答案没有尽早检测到震动。 操作方法如下:

  1. 在目标的构建阶段将 CoreMotion 链接到您的项目:
    CoreMotion
  2. 在你的 ViewController 中:

    - (BOOL)canBecomeFirstResponder { 
          返回是; 
      } 
    
      - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event 
      { 
          if (motion == UIEventSubtypeMotionShake) { 
              // 检测到震动。 
          } 
      } 
      

First off, I know this is an old post, but it is still relevant, and I found that the two highest voted answers did not detect the shake as early as possible. This is how to do it:

  1. Link CoreMotion to your project in the target's build phases:
    CoreMotion
  2. In your ViewController:

    - (BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake) {
            // Shake detected.
        }
    }
    
定格我的天空 2024-07-13 01:03:01

很抱歉将此作为答案而不是评论发布,但正如您所看到的,我是 Stack Overflow 的新手,所以我还没有足够的信誉来发表评论!

不管怎样,我同意@cire,一旦视图成为视图层次结构的一部分,就确保设置第一响应者状态。 因此,在视图控制器 viewDidLoad 方法中设置第一响应者状态将不起作用。 如果您不确定它是否正常工作,[viewBecomeFirstResponder] 会返回一个可以测试的布尔值。

另一点:如果您不想不必要地创建 UIView 子类,您可以使用视图控制器来捕获摇动事件。 我知道这并没有那么麻烦,但仍然有选择。 只需将 Kendall 放入 UIView 子类的代码片段移至控制器中,并将 becomeFirstResponderresignFirstResponder 消息发送到 self 而不是 UIView 子类。

Sorry to post this as an answer rather than a comment but as you can see I'm new to Stack Overflow and so I'm not yet reputable enough to post comments!

Anyway I second @cire about making sure to set the first responder status once the view is part of the view hierarchy. So setting first responder status in your view controllers viewDidLoad method won't work for example. And if you're unsure as to whether it is working [view becomeFirstResponder] returns you a boolean that you can test.

Another point: you can use a view controller to capture the shake event if you don't want to create a UIView subclass unnecessarily. I know it's not that much hassle but still the option is there. Just move the code snippets that Kendall put into the UIView subclass into your controller and send the becomeFirstResponder and resignFirstResponder messages to self instead of the UIView subclass.

开始看清了 2024-07-13 01:03:01

我发现这篇文章正在寻找“震撼”的实现。 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);
}

PS:
我已将更新间隔设置为 1/15 秒。

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

I came across this post looking for a "shaking" implementation. millenomi's answer worked well for me, although i was looking for something that required a bit more "shaking action" to trigger. I've replaced to Boolean value with an int shakeCount. I also reimplemented the L0AccelerationIsShaking() method in Objective-C. You can tweak the ammount of shaking required by tweaking the ammount added to shakeCount. I'm not sure i've found the optimal values yet, but it seems to be working well so far. Hope this helps someone:

- (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);
}

PS:
I've set the update interval to 1/15th of a second.

[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 15)];
隔岸观火 2024-07-13 01:03:01

从我的 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

滞后现象可防止多次触发摇动事件,直到用户停止摇动。

From my Diceshaker application:

// 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

The histeresis prevents the shake event from triggering multiple times until the user stops the shake.

携君以终年 2024-07-13 01:03:01

我终于使用此 撤消/重做管理器教程
这正是您需要做的:

  • 在应用程序的委托中设置 applicationSupportsShakeToEdit 属性:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    

  • 添加/覆盖 canBecomeFirstResponderviewDidAppear:viewWillDisappear: 视图控制器中的方法:
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    

  • motionEnded 方法添加到您的视图控制器中:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    

    I finally made it work using code examples from this Undo/Redo Manager Tutorial.
    This is exactly what you need to do:

  • Set the applicationSupportsShakeToEdit property in the App's Delegate:
  • 
        - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
            application.applicationSupportsShakeToEdit = YES;
    
            [window addSubview:viewController.view];
            [window makeKeyAndVisible];
    }
    
  • Add/Override canBecomeFirstResponder, viewDidAppear: and viewWillDisappear: methods in your View Controller:
  • 
    -(BOOL)canBecomeFirstResponder {
        return YES;
    }
    
    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self becomeFirstResponder];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [self resignFirstResponder];
        [super viewWillDisappear:animated];
    }
    
  • Add the motionEnded method to your View Controller:
  • 
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (motion == UIEventSubtypeMotionShake)
        {
            // your code
        }
    }
    
    泪冰清 2024-07-13 01:03:01

    首先,肯德尔 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: 中。 确保您对摇动事件的响应知道它是否“已在进行中”。 否则,如果设备连续晃动两次,就会出现轻微的交通拥堵。 这样您就可以忽略其他通知,直到您真正完成对原始通知的响应。

    另外:您可以选择提示 motionBeganmotionEnded。 由你决定。 就我而言,效果总是需要在设备处于静止状态(相对于设备开始晃动时)之后发生,因此我使用motionEnded。 尝试两者,看看哪一个更有意义……或者检测/通知两者!

    这里还有一个(好奇?)观察:请注意,此代码中没有第一响应者管理的迹象。 到目前为止,我只尝试过使用表视图控制器,一切似乎都配合得很好! 但我不能保证其他情况。

    肯德尔等。 al - 谁能解释一下为什么 UIWindow 子类会出现这种情况? 是因为窗户位于食物链的顶端吗?

    First, Kendall's July 10th answer is spot-on.

    Now ... I wanted to do something similar (in iPhone OS 3.0+), only in my case I wanted it app-wide so I could alert various parts of the app when a shake occurred. Here's what I ended up doing.

    First, I subclassed UIWindow. This is easy peasy. Create a new class file with an interface such as MotionWindow : UIWindow (feel free to pick your own, natch). Add a method like so:

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

    Change @"DeviceShaken" to the notification name of your choice. Save the file.

    Now, if you use a MainWindow.xib (stock Xcode template stuff), go in there and change the class of your Window object from UIWindow to MotionWindow or whatever you called it. Save the xib. If you set up UIWindow programmatically, use your new Window class there instead.

    Now your app is using the specialized UIWindow class. Wherever you want to be told about a shake, sign up for them notifications! Like this:

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

    To remove yourself as an observer:

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    I put mine in viewWillAppear: and viewWillDisappear: where View Controllers are concerned. Be sure your response to the shake event knows if it is "already in progress" or not. Otherwise, if the device is shaken twice in succession, you'll have a li'l traffic jam. This way you can ignore other notifications until you're truly done responding to the original notification.

    Also: You may choose to cue off of motionBegan vs. motionEnded. It's up to you. In my case, the effect always needs to take place after the device is at rest (vs. when it starts shaking), so I use motionEnded. Try both and see which one makes more sense ... or detect/notify for both!

    One more (curious?) observation here: Notice there's no sign of first responder management in this code. I've only tried this with Table View Controllers so far and everything seems to work quite nicely together! I can't vouch for other scenarios though.

    Kendall, et. al - can anyone speak to why this might be so for UIWindow subclasses? Is it because the window is at the top of the food chain?

    小女人ら 2024-07-13 01:03:00

    在 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,此方法也有效。

    In 3.0, there's now an easier way - hook into the new motion events.

    The main trick is that you need to have some UIView (not UIViewController) that you want as firstResponder to receive the shake event messages. Here's the code that you can use in any UIView to get shake events:

    @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
    

    You can easily transform any UIView (even system views) into a view that can get the shake event simply by subclassing the view with only these methods (and then selecting this new type instead of the base type in IB, or using it when allocating a view).

    In the view controller, you want to set this view to become first responder:

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

    Don't forget that if you have other views that become first responder from user actions (like a search bar or text entry field) you'll also need to restore the shaking view first responder status when the other view resigns!

    This method works even if you set applicationSupportsShakeToEdit to NO.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文