iOS Motion Detection: Motion Detection Sensitivity Levels

后端 未结 4 1447
我在风中等你
我在风中等你 2021-02-05 18:24

I have a simple question. I\'m trying to detect when a user shakes the iPhone. I have the standard code in place to detect the motion and this works no problem. However, in test

4条回答
  •  栀梦
    栀梦 (楼主)
    2021-02-05 18:36

    This is a swift version based on zic10's answer, with the addition of a flag that prevents getting a few extra calls to your motion handler even when the first line in that handler is motionManager.stopDeviceMotionUpdates().

    Also, a value of around 3.0 can be useful if you want to ignore the shake, but detect a bump. I found 0.3 to be way too low as it ended up being more like "detect move". In my tests, the ranges were more like:

    • 0.75 - 2.49 is a better range for shake sensitivity
    • 2.5 - 5.0 is a good range for "ignore shake, detect bump"

    Here is the complete view controller for an Xcode single VC template:

    import UIKit
    import CoreMotion
    
    class ViewController: UIViewController {
    
        lazy var motionManager: CMMotionManager = {
            return CMMotionManager()
        }()
    
        let accelerationThreshold = 3.0
    
        var handlingShake = false
    
        override func viewWillAppear(animated: Bool) {
            handlingShake = false
            motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.currentQueue()!) { [weak self] (motion, error) in
                if
                    let userAcceleration = motion?.userAcceleration,
                    let _self = self {
    
                    print("\(userAcceleration.x) / \(userAcceleration.y)")
    
                    if (fabs(userAcceleration.x) > _self.accelerationThreshold
                        || fabs(userAcceleration.y) > _self.accelerationThreshold
                        || fabs(userAcceleration.z) > _self.accelerationThreshold)
                    {
                        if !_self.handlingShake {
                            _self.handlingShake = true
                            _self.handleShake();
                        }
                    }
    
                } else {
                    print("Motion error: \(error)")
                }
            }
        }
    
        override func viewWillDisappear(animated: Bool) {
            // or wherever appropriate
            motionManager.stopDeviceMotionUpdates()
        }
    
        func handleShake() {
            performSegueWithIdentifier("showShakeScreen", sender: nil)
        }
    
    }
    

    And the storyboard I used for this test looks like this:

    It's also worth noting that CoreMotion is not testable in the simulator. Because of this constraint you may still find it worthwhile to additionally implement the UIDevice method of detecting motion shake. This would allow you to manually test shake in the simulator or give UITests access to shake for testing or tools like fastlane's snapshot. Something like:

    class ViewController: UIViewController {
    
        override func viewDidAppear(animated: Bool) {
            super.viewDidAppear(animated)
            becomeFirstResponder()
        }
    
        override func canBecomeFirstResponder() -> Bool {
            return true
        }
    
        override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
            if TARGET_OS_SIMULATOR != 0 {
                if event?.subtype == .MotionShake {
                    // do stuff
                }
            }
        }
    
    }
    

    And then use Ctrl-Cmd-Z to test shake in the simulator.

提交回复
热议问题