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?
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.
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()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With