Is there any way to monitor user's movement of iphone like the “raise to speak” feature?

大兔子大兔子 提交于 2019-12-09 06:35:19

问题


I want to get notified when the user raises the iphone to his face. Just like siri does. Is it possible?

Add more specific requirement: I want to darken the screen when user put the phone near his ear. I know the Proximity Sensor can be enable to implement this. But it's annoying that the screen will be darkened from time to time when user moves the finger upon the sensor. So I wonder how to avoid this case and only darken the screen when user raise the iphone to speak?


回答1:


See Using the Proximity Sensor in the UIDevice Class Reference. So, you:

  • Enable it:

    UIDevice *device = [UIDevice currentDevice];
    device.proximityMonitoringEnabled = YES;
    
  • Check if successfully enabled; observe the UIDeviceProximityStateDidChangeNotification notification if successful; if not, your device may not be capable:

    if (device.proximityMonitoringEnabled)
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleProximityChange:)
                                                     name:UIDeviceProximityStateDidChangeNotification
                                                   object:nil];
    }
    else
    {
         // device not capable
    }
    
  • And write your selector:

    - (void)handleProximityChange:(NSNotification *)notification
    {
        NSLog(@"%s proximityState=%d", __FUNCTION__, [[UIDevice currentDevice] proximityState]);
    }
    

To detect whether the user is holding it up to their face, I might marry the proximity sensor with the CMMotionManager and look at the gravity property to see if they're holding the phone nearly vertically. So, define a few class properties:

@property (nonatomic, strong) CMMotionManager *motionManager;
@property (nonatomic, strong) NSOperationQueue *deviceQueue;

And then you can start the CMMotionManager, looking for whether the device is held in a vertical position:

self.deviceQueue = [[NSOperationQueue alloc] init];
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0;

UIDevice *device = [UIDevice currentDevice];

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical
                                                        toQueue:self.deviceQueue
                                                    withHandler:^(CMDeviceMotion *motion, NSError *error)
{
    BOOL vertical = (motion.gravity.z > -0.4 && motion.gravity.z < 0.4 & motion.gravity.y < -0.7);
    if ((vertical && !device.proximityMonitoringEnabled) || (!vertical && device.proximityMonitoringEnabled))
    {
        device.proximityMonitoringEnabled = vertical;
    }
}];

Whether these gravity thresholds make sense is a little subjective. You could also, rather than just looking to see if the phone is being held roughly vertically, look at other accelerometer data (e.g. did they raise the object or not). Seems like there are lots of ways to skin the cat.




回答2:


I know this is old, but I simplified the logic a bit and made a wrapper class in Swift.

You can find it here

class DeviceRaisedToEarListener: NSObject {
private let deviceQueue = NSOperationQueue()
private let motionManager = CMMotionManager()
private var vertical: Bool = false

private(set) var isRaisedToEar: Bool = false {
    didSet {
        if oldValue != self.isRaisedToEar {
            self.stateChanged?(isRaisedToEar: self.isRaisedToEar)
        }
    }
}

var stateChanged:((isRaisedToEar: Bool)->())? = nil

override init() {
    super.init()
    self.setupMotionManager()
}

private func setupMotionManager() {
    self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0
    let device = UIDevice.currentDevice()

    // Only listen for proximity changes if the device is held vertically
    self.motionManager.startDeviceMotionUpdatesUsingReferenceFrame(CMAttitudeReferenceFrame.XArbitraryZVertical, toQueue: self.deviceQueue) { (motion, error) in
        self.vertical = (motion.gravity.z > -0.4 && motion.gravity.z < 0.4 && motion.gravity.y < -0.7)
    }
}

func startListening() {
    UIDevice.currentDevice().proximityMonitoringEnabled = true
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleProximityChange:", name: UIDeviceProximityStateDidChangeNotification, object: nil)
}

func stopListening() {
    UIDevice.currentDevice().proximityMonitoringEnabled = false
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func handleProximityChange(notification: NSNotification) {
    self.isRaisedToEar = UIDevice.currentDevice().proximityState && self.vertical
}

deinit {
    self.stopListening()
}
}


// How to use:
private func setupRaiseListener() {
    self.deviceListener.stateChanged = { [weak self] isRaisedToEar in
      println("is raised? \(isRaisedToEar)")
    }
    self.deviceListener.startListening()
}


来源:https://stackoverflow.com/questions/15490385/is-there-any-way-to-monitor-users-movement-of-iphone-like-the-raise-to-speak

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!