Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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()
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top