美文网首页Cocoapods
iPhone 接近感应器的坑及解决方案

iPhone 接近感应器的坑及解决方案

作者: 来自北方的羊 | 来源:发表于2015-11-29 02:10 被阅读3023次

    原文发布于我的博客:http://blog.zyliu.com/ios-proximity-state-bug-and-solution/

    实习的时候写公司产品,有个用到接近感应器的功能。就比如打电话,电话接通时开启接近感应器,侦测到接近状态改变(接近/离开)时执行相应的操作——当开启接近感应器时,系统会在接近时熄灭屏幕,离开时再点亮屏幕,等等。但是在这个过程中有bug存在,导致系统接口给的结果不一定是准确的。

    先说理论上的实现

    UIKit/UIDevice.h 中的 UIDevice 类(iOS 8.4 SDK),有如下属性

    @property(nonatomic,getter=isProximityMonitoringEnabled) BOOL proximityMonitoringEnabled NS_AVAILABLE_IOS(3_0); // default is NO
    @property(nonatomic,readonly)                            BOOL proximityState NS_AVAILABLE_IOS(3_0);  // always returns NO if no proximity detector
    

    proximityMonitoringEnabled 用来标识是否开启接近感应器,如果为 YES 则开启。proximityState 为当前的接近状态,如果为 YES 则为接近(触发),否则为离开(未触发),需要的时候可以直接拿来用。

    以及用于 UINotificationCenter 的键:

    UIKIT_EXTERN NSString *const UIDeviceProximityStateDidChangeNotification NS_AVAILABLE_IOS(3_0);
    

    于是如果需要在某个地方使用接近感应器,可以注册通知并且开启接近感应器:

    UIDevice *device = [UIDevice currentDevice];
    [device setProximityMonitoringEnabled:YES];
    if ([device isProximityMonitoringEnabled]) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(proximityStateDidChange:)
                                                     name:UIDeviceProximityStateDidChangeNotification object:nil];
    }else {
        NSLog(@"No Proximity Sensor");
    }
    

    然后在 -(void)proximityStateDidChange:(BOOL) 方法里面实现需要做的处理。当然记得在不需要的时候取消注册通知、停止接近检测。

    这样看起来是没问题的。

    突然问题来了

    直到有一天发现了一个问题:在已经开启接近检测的情况下,同时触发接近感应器和进入后台(按 Home 键的同时捂住听筒),这样会在熄灭屏幕的情况下进入了后台。手离开听筒,屏幕再次点亮。再进入前台,发现 [UIDevice currentDevice].proximityState 的值为 YES 。也就是说,触发接近感应器的同时进入后台,在后台时离开接近感应器是不会刷新接近状态的(会保持在触发状态)。在这种情况下,系统提供的接口结果不正确。经验证,从 iOS 7iOS 9 都存在这个问题。只有再次触发接近感应器并离开时,才会收到 UIDeviceProximityStateDidChangeNotification 通知,也就是变回正常了。这样看来,UIDeviceproximityState 属性也是依赖于上面的通知更新吧。

    解决方案

    好了,既然 iOS 留下了这个 Bug,下面就是如何想办法解决它。对于接近感应器,我们需要的很简单,就是在任何情况下拿到的数据都是真实有效的。有这样一种思路:每次从后台进入前台时,是用户在屏幕亮着并且可操作的情况下进来的,进来之前的瞬间不可能是接近状态。所以在进入前台后到收到接近状态改变的通知前的这段时间,可以推测是非接近状态。

    在这样的思路下,我们就可以做一个简单的接近检测的工具类,添加一个 proximityState 属性,将 get 方法写为:

    - (BOOL)proximityState{
        if (self.isWaitingProximityStateUpdate) {
            return NO;
        }else {
            return [[UIDevice currentDevice] proximityState];
        }
    }
    

    其中,isWaitingProximityStateUpdate 表示是否为进入前台后到收到接近状态改变通知前的这段时间。每次进入前台就把这个属性置为 YES,收到接近状态改变通知就置为 NO

    这样,通过这个工具类的 proximityState 属性,在任何情况下拿到的结果都是真实有效的。

    相关文章

      网友评论

        本文标题:iPhone 接近感应器的坑及解决方案

        本文链接:https://www.haomeiwen.com/subject/ndtjhttx.html