iOS + 傳感器的使用(加速計(jì)、陀螺儀、計(jì)步器等)

一. 簡介

iOS設(shè)備內(nèi)置了一些傳感器,并提供了相關(guān)的API供調(diào)用,關(guān)于iOS傳感器的類型及作用等相關(guān)知識(shí),可以查看文章iOS開發(fā)之傳感器 ,本篇文章的demo的github地址是:傳感器的使用,感興趣的可以瞄瞄。

傳感器 涉及的類 作用
光線傳感器(Ambient Light ) UIScreen、AVCaptureSession 自動(dòng)調(diào)節(jié)屏幕亮度、手電筒的開啟(mobile有)
距離傳感器(Proximity) UIDevice 打電話自動(dòng)鎖屏
計(jì)步器(Pedometer) CMPedometer 運(yùn)動(dòng)數(shù)據(jù)測量
加速計(jì)(Accelerometer) CMMotionManager 計(jì)步器、搖一搖
陀螺儀(Gyroscope) CMMotionManager 游戲控制
磁力計(jì)(Magnetometer) CMMotionManager 磁力感應(yīng)
濕度傳感器(Moisture) 維修時(shí)檢測設(shè)備進(jìn)水與否
內(nèi)部溫度傳感器(Internal Temperature)
SensorDemo

二. 光線強(qiáng)弱測量

蘋果有相關(guān)的私有api提供光線檢測,但不允許上架產(chǎn)品使用,所以間接地使用其它方式檢測光線強(qiáng)弱,有興趣的小伙伴可以去研究下<GraphicsServices/GraphicsServices.h>中的GSEventSetBacklightLevel()方法

  1. 檢測屏幕亮度
    1. UIScreenBrightnessDidChangeNotification:光線變化通知
    2. [UIScreen mainScreen].brightness:獲取屏幕亮度

    光線亮度調(diào)節(jié)
    1. 對(duì)手機(jī)自動(dòng)亮度調(diào)節(jié)進(jìn)行設(shè)置,設(shè)置-->顯示與亮度-->允許自動(dòng)亮度調(diào)節(jié),當(dāng)手機(jī)感受到外界光線亮度變化時(shí),會(huì)自動(dòng)調(diào)節(jié)屏幕亮度
    2. 手動(dòng)調(diào)節(jié)屏幕亮度
    
  2. 攝像頭檢測
    "通過對(duì)攝像頭捕獲的視頻流的參數(shù)進(jìn)行分析,亮度的參數(shù)key是kCGImagePropertyExifBrightnessValue"

    // AVCaptureVideoDataOutputSampleBufferDelegate代理方法
    // 攝像頭輸出的視頻流
    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
        // 獲取視頻流的相關(guān)參數(shù)
        CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
        NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
        CFRelease(metadataDict);
        
        "exif中有個(gè)brightness參數(shù)值"
        NSDictionary *exifMetadata = [metadata[(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
        float brightnessValue = [exifMetadata[(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
    
        // 在UI上顯示
        self.cameraBightnessLbl.text = [NSString stringWithFormat:@"%.2f",brightnessValue];
    }
    

順便將攝像頭捕獲視頻流的方法貼出來
- (AVCaptureSession *)initCaptureSession {
// 創(chuàng)建會(huì)話
NSError *error;
AVCaptureSession *captureSession = [AVCaptureSession new];
_captureSession = captureSession;

        // 輸入:攝像頭
        AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        AVCaptureDeviceInput *cameraDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:&error];
        if ([captureSession canAddInput:cameraDeviceInput]) {
            [captureSession addInput:cameraDeviceInput];
        }
        
        // 輸出:視頻數(shù)據(jù)
        "當(dāng)有視頻數(shù)據(jù)輸出時(shí),會(huì)調(diào)用上面的AVCaptureVideoDataOutputSampleBufferDelegate代理方法"
        AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
        [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
        if ([captureSession canAddOutput:output]) {
            [captureSession addOutput:output];
        }
        
        // 實(shí)時(shí)預(yù)覽:展現(xiàn)給用戶
        AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
        previewLayer.frame = self.lightVideoV.bounds;
        [self.lightVideoV.layer addSublayer:previewLayer];
    }

三. 距離傳感器檢測

    1. 距離傳感器默認(rèn)是關(guān)閉的,需手動(dòng)開啟
       [UIDevice currentDevice].proximityMonitoringEnabled = YES/NO;

    2. 傳感器開啟后,通過通知監(jiān)聽是否有物體靠近
        1. 通知名:UIDeviceProximityStateDidChangeNotification;
        2. 返回對(duì)象:notification.object會(huì)返回UIDevice對(duì)象;
        3. 判斷:UIDevice對(duì)象的proximityState屬性指示是否靠近用戶,YES(靠近)/NO(離開)

    3. 代碼
        - (IBAction)proximitySwitch:(UISwitch *)sender {
            // 傳感器的開啟與關(guān)閉
            [UIDevice currentDevice].proximityMonitoringEnabled = sender.on;    

            // 可用性檢測
            if (sender.on && ![UIDevice currentDevice].proximityMonitoringEnabled) {
                sender.on = NO;
                [self showWithTitle:@"距離傳感器不可用" message:nil];
                return;
            }

            // 通知監(jiān)聽狀態(tài)
            if (sender.on) {
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateChange:) name:UIDeviceProximityStateDidChangeNotification object:nil
                 ];
            } else {
                [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceProximityStateDidChangeNotification object:nil];
            }
        }
        
        - (void)proximityStateChange:(NSNotification *)noti {
            UIDevice *device = noti.object;
            if ([device isKindOfClass:[UIDevice class]]) {
                // 是否有物體靠近
                NSLog(@"%@", (device.proximityState? @"物體靠近" : @"物體離開"));
            }
        }

四. 計(jì)步器

ios7-ios8的時(shí)候使用CMStepCounter,ios8及以后使用CMPedometer,下面介紹的是CMPedometer

  1. 計(jì)步器能獲取的數(shù)據(jù)
    1. startDate:開始時(shí)間段
    2. endDate:結(jié)束時(shí)間段
    3. numberOfSteps:步數(shù)
    4. distance:距離
    5. floorsAscended:爬樓數(shù)
    6. floorsDescended:下樓數(shù)
    7. ios9以后的系統(tǒng)才有
    1. currentPace:當(dāng)前步速 in seconds per meter
    2. currentCadence:當(dāng)前步頻 in steps per second
    8. ios10的
    1. averageActivePace:平均活躍時(shí)間段的步速

  2. 使用

    1. "運(yùn)動(dòng)與健康"權(quán)限設(shè)置
      1. 使用計(jì)步器需添加權(quán)限NSMotionUsageDescription描述
      2. 第一次使用CMPedometer對(duì)象的時(shí)候系統(tǒng)自動(dòng)會(huì)向用戶請求"運(yùn)動(dòng)與健康"授權(quán)
      3. 授權(quán)判斷:[CMSensorRecorder isAuthorizedForRecording];
      4. 沒找到授權(quán)的方法:不過由于系統(tǒng)會(huì)自動(dòng)授權(quán)(第2條),通過下面的方法也可以達(dá)到授權(quán)的效果,
      該方法在獲取到用戶的選擇之后才會(huì)進(jìn)行回調(diào)
      [self.pedometer queryPedometerDataFromDate:toDate:withHandler:]

    2. 獲取數(shù)據(jù)
      1. CMPedometer中的對(duì)象方法
      // 獲取某時(shí)間段的歷史數(shù)據(jù)
      - (void)queryPedometerDataFromDate:(NSDate *)start toDate:(NSDate *)end withHandler:(CMPedometerHandler)handler;
      // 從某時(shí)間段開始實(shí)時(shí)獲取數(shù)據(jù),停止獲取的時(shí)候需調(diào)用stopPedometerUpdates方法
      - (void)startPedometerUpdatesFromDate:(NSDate *)start withHandler:(CMPedometerHandler)handler;
      // 獲取計(jì)步器的狀態(tài)(暫停/記步),停止獲取的時(shí)候需調(diào)用stopPedometerEventUpdates方法
      - (void)startPedometerEventUpdatesWithHandler:(CMPedometerEventHandler)handler NS_AVAILABLE(NA,10_0) __WATCHOS_AVAILABLE(3_0);
      // 停止更新數(shù)據(jù)
      - (void)stopPedometerUpdates;
      // 停止?fàn)顟B(tài)更新
      - (void)stopPedometerEventUpdates NS_AVAILABLE(NA,10_0);

       2. 主要的代碼實(shí)現(xiàn)
           // 1. 初始化計(jì)步器self.pedometer
           - (CMPedometer *)pedometer {
                  if (!_pedometer) {
                      _pedometer = [[CMPedometer alloc] init];
                  }
                 return _pedometer;
             }
      
            // 2. 通過一個(gè)開關(guān)開啟/關(guān)閉記步功能
           - (IBAction)recordStepCount:(UIButton *)sender {
                 BOOL start = !sender.selected;
                 // 開始記步
                 if(start){
      
                     // 可用性檢測
                     if(![CMPedometer isStepCountingAvailable]){
                         [self showWithTitle:@"計(jì)步器不可用" message:nil];
                        return;
                     }
      
                     // 1 授權(quán)
                     // 1.1 pedometer第一次被使用時(shí),會(huì)由系統(tǒng)主動(dòng)提示用戶授權(quán)“運(yùn)動(dòng)與健康”;但沒找到授權(quán)的相關(guān)方法,通過該方式實(shí)現(xiàn)需求
                     __weak typeof (self) weakSelf = self;
                     [self.pedometer queryPedometerDataFromDate:[NSDate date] toDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
                         // 1.2 用戶選擇了授權(quán)與否之后,該block才會(huì)被調(diào)用,不在主線程
                         dispatch_async(dispatch_get_main_queue(), ^{
                             // 1.3 授權(quán)判斷
                             if(![CMSensorRecorder isAuthorizedForRecording]){
                                 [weakSelf showWithTitle:@"未授權(quán)" message:@"前往設(shè)置->隱私->運(yùn)動(dòng)與健康,點(diǎn)擊允許訪問"];
                                 return;
                             }
                             sender.selected = YES;
                         
                             // 2. 獲取數(shù)據(jù)
                             // 2.1 監(jiān)測計(jì)步器狀態(tài):暫停、恢復(fù)
                             [weakSelf.pedometer startPedometerEventUpdatesWithHandler:^(CMPedometerEvent * _Nullable pedometerEvent, NSError * _Nullable error) {
                                 NSLog(@"%@",pedometerEvent.type==CMPedometerEventTypePause? @"暫停":@"繼續(xù)");
                             }];
                         
                             // 2.2 監(jiān)測計(jì)步器數(shù)據(jù)
                             [weakSelf.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
                                 if (pedometerData) {
                                     // 2.3 處理數(shù)據(jù):回調(diào)不在主線程,所以需要回到主線程處理
                                     dispatch_async(dispatch_get_main_queue(), ^{
                                         // 數(shù)據(jù)存儲(chǔ)在pedometerData中
                                         ...
                                     });
                                 }
                             }];
                         });
                     }];
                 } else {
                     // 結(jié)束記步
                     sender.selected = NO;
                     [self.pedometer stopPedometerUpdates];
                     [self.pedometer stopPedometerEventUpdates];
                 }
             }
      

四. 加速計(jì)/陀螺儀/磁力計(jì)

  1. 加速計(jì)/陀螺儀/磁力計(jì)這三種感應(yīng)器使用也很簡單,直接通過CMMotionManager對(duì)象處理,獲取數(shù)據(jù)的方法大同小異;
  1. CMMotionManager對(duì)象可以檢測設(shè)備的可用性、獲取數(shù)據(jù)、設(shè)置數(shù)據(jù)的更新頻率,有需要的可以直接去頭文件看看
  2. 通過計(jì)步器制作搖一搖、計(jì)步器:按我理解呢,如果考慮周全,搖一搖、記步也需要比較復(fù)雜的算法,自己做難免不全面;并且這些蘋果已經(jīng)有對(duì)應(yīng)API,直接調(diào)用就好
  3. CMMotion的使用可以參考這篇文章詳說CMDeviceMotion

由于這三種感應(yīng)器獲取數(shù)據(jù)方式一致,就只對(duì)加速計(jì)進(jìn)行舉例說明

  1. 成員變量及方法

     // 存儲(chǔ)加速計(jì)數(shù)據(jù)
    @property(readonly, nullable) CMAccelerometerData *accelerometerData;
    
    // 開始更新加速計(jì)數(shù)據(jù),不帶回調(diào),可以添加定時(shí)器定時(shí)去獲取CMMotionManager對(duì)象的accelerometerData數(shù)據(jù)
    - (void)startAccelerometerUpdates;
    
    // 開始更新加速計(jì)數(shù)據(jù),帶回調(diào),由于數(shù)據(jù)可能更新頻率快,不建議使用主隊(duì)列
    - (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler;
    
    // 不再需要更新數(shù)據(jù)的時(shí)候需要調(diào)用停止更新的方法
    - (void)stopAccelerometerUpdates;
    
  2. 使用步驟
    // 1. 初始化CMMotionManager對(duì)象并設(shè)置屬性存儲(chǔ),設(shè)置數(shù)據(jù)的更新間隔
    - (CMMotionManager *)motionManage {
    if (!_motionManage) {
    _motionManage = [[CMMotionManager alloc] init];
    // 控制傳感器的更新間隔
    _motionManage.accelerometerUpdateInterval = 0.2;
    _motionManage.gyroUpdateInterval = 0.2;
    _motionManage.magnetometerUpdateInterval = 0.2;
    }
    return _motionManage;
    }

    // 2. 開始/結(jié)束更新數(shù)據(jù),只舉例帶回調(diào)的方法
    - (IBAction)accelerometerTest:(UIButton *)sender {
        BOOL start = !sender.selected;
    
        // 2.1 根據(jù)設(shè)置的時(shí)間間隔定期更新數(shù)據(jù)
        if (start) {
            // 可用性檢測
            if(![self.motionManage isAccelerometerAvailable]){
                [self showWithTitle:@"加速計(jì)不可用" message:nil];
                return;
            }
            sender.selected = YES;
            __weak typeof (self) weakSelf = self;
    
            // 數(shù)據(jù)更新有可能比較頻繁,不建議使用主隊(duì)列
            NSOperationQueue *queue = [NSOperationQueue new];
            [self.motionManage startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
                // 回到主線程
                dispatch_async(dispatch_get_main_queue(), ^{
                    // 數(shù)據(jù)顯示
                    weakSelf.accelerationXLbl.text = [NSString stringWithFormat:@"%.2f", accelerometerData.acceleration.x];
                    weakSelf.accelerationYLbl.text = [NSString stringWithFormat:@"%.2f", accelerometerData.acceleration.y];
                    weakSelf.accelerationZLbl.text = [NSString stringWithFormat:@"%.2f", accelerometerData.acceleration.z];
                });
            }];
        } else {
    
        // 2.2 停止獲取數(shù)據(jù)
            sender.selected = NO;
            [self.motionManage stopAccelerometerUpdates];
        }
    }
    
  3. 自帶搖一搖功能
    UIResponder類中已經(jīng)封裝好了搖一搖功能,當(dāng)對(duì)象成為第一響應(yīng)者之后,
    系統(tǒng)就會(huì)通知對(duì)象搖一搖的開始/結(jié)束狀態(tài),實(shí)現(xiàn)以下方法就可以

    - (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,417評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,267評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,471評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,698評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評(píng)論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,204評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,436評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容