iPhone 接近感應(yīng)器的坑及解決方案

原文發(fā)布于我的博客:http://blog.zyliu.com/ios-proximity-state-bug-and-solution/

實(shí)習(xí)的時(shí)候?qū)懝井a(chǎn)品,有個(gè)用到接近感應(yīng)器的功能。就比如打電話(huà),電話(huà)接通時(shí)開(kāi)啟接近感應(yīng)器,偵測(cè)到接近狀態(tài)改變(接近/離開(kāi))時(shí)執(zhí)行相應(yīng)的操作——當(dāng)開(kāi)啟接近感應(yīng)器時(shí),系統(tǒng)會(huì)在接近時(shí)熄滅屏幕,離開(kāi)時(shí)再點(diǎn)亮屏幕,等等。但是在這個(gè)過(guò)程中有bug存在,導(dǎo)致系統(tǒng)接口給的結(jié)果不一定是準(zhǔn)確的。

先說(shuō)理論上的實(shí)現(xiàn)

UIKit/UIDevice.h 中的 UIDevice 類(lèi)(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 用來(lái)標(biāo)識(shí)是否開(kāi)啟接近感應(yīng)器,如果為 YES 則開(kāi)啟。proximityState 為當(dāng)前的接近狀態(tài),如果為 YES 則為接近(觸發(fā)),否則為離開(kāi)(未觸發(fā)),需要的時(shí)候可以直接拿來(lái)用。

以及用于 UINotificationCenter 的鍵:

UIKIT_EXTERN NSString *const UIDeviceProximityStateDidChangeNotification NS_AVAILABLE_IOS(3_0);

于是如果需要在某個(gè)地方使用接近感應(yīng)器,可以注冊(cè)通知并且開(kāi)啟接近感應(yīng)器:

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) 方法里面實(shí)現(xiàn)需要做的處理。當(dāng)然記得在不需要的時(shí)候取消注冊(cè)通知、停止接近檢測(cè)。

這樣看起來(lái)是沒(méi)問(wèn)題的。

突然問(wèn)題來(lái)了

直到有一天發(fā)現(xiàn)了一個(gè)問(wèn)題:在已經(jīng)開(kāi)啟接近檢測(cè)的情況下,同時(shí)觸發(fā)接近感應(yīng)器和進(jìn)入后臺(tái)(按 Home 鍵的同時(shí)捂住聽(tīng)筒),這樣會(huì)在熄滅屏幕的情況下進(jìn)入了后臺(tái)。手離開(kāi)聽(tīng)筒,屏幕再次點(diǎn)亮。再進(jìn)入前臺(tái),發(fā)現(xiàn) [UIDevice currentDevice].proximityState 的值為 YES 。也就是說(shuō),觸發(fā)接近感應(yīng)器的同時(shí)進(jìn)入后臺(tái),在后臺(tái)時(shí)離開(kāi)接近感應(yīng)器是不會(huì)刷新接近狀態(tài)的(會(huì)保持在觸發(fā)狀態(tài))。在這種情況下,系統(tǒng)提供的接口結(jié)果不正確。經(jīng)驗(yàn)證,從 iOS 7iOS 9 都存在這個(gè)問(wèn)題。只有再次觸發(fā)接近感應(yīng)器并離開(kāi)時(shí),才會(huì)收到 UIDeviceProximityStateDidChangeNotification 通知,也就是變回正常了。這樣看來(lái),UIDeviceproximityState 屬性也是依賴(lài)于上面的通知更新吧。

解決方案

好了,既然 iOS 留下了這個(gè) Bug,下面就是如何想辦法解決它。對(duì)于接近感應(yīng)器,我們需要的很簡(jiǎn)單,就是在任何情況下拿到的數(shù)據(jù)都是真實(shí)有效的。有這樣一種思路:每次從后臺(tái)進(jìn)入前臺(tái)時(shí),是用戶(hù)在屏幕亮著并且可操作的情況下進(jìn)來(lái)的,進(jìn)來(lái)之前的瞬間不可能是接近狀態(tài)。所以在進(jìn)入前臺(tái)后到收到接近狀態(tài)改變的通知前的這段時(shí)間,可以推測(cè)是非接近狀態(tài)。

在這樣的思路下,我們就可以做一個(gè)簡(jiǎn)單的接近檢測(cè)的工具類(lèi),添加一個(gè) proximityState 屬性,將 get 方法寫(xiě)為:

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

其中,isWaitingProximityStateUpdate 表示是否為進(jìn)入前臺(tái)后到收到接近狀態(tài)改變通知前的這段時(shí)間。每次進(jìn)入前臺(tái)就把這個(gè)屬性置為 YES,收到接近狀態(tài)改變通知就置為 NO

這樣,通過(guò)這個(gè)工具類(lèi)的 proximityState 屬性,在任何情況下拿到的結(jié)果都是真實(shí)有效的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,219評(píng)論 4 61
  • 我的2018關(guān)鍵詞是“有序、改變、玩樂(lè)”! 忙碌的2017還沒(méi)來(lái)得及整理出情緒來(lái)緬懷,2018已經(jīng)鮮活的的撲面...
    流浪貝貝閱讀 221評(píng)論 0 2

友情鏈接更多精彩內(nèi)容