iOS + 传感器的使用(加速计、陀螺仪、计步器等)

一. 简介

iOS设备内置了一些传感器,并提供了相关的API供调用,关于iOS传感器的类型及作用等相关知识,可以查看文章iOS开发之传感器 ,本篇文章的demo的github地址是:传感器的使用,感兴趣的可以瞄瞄。

传感器 涉及的类 作用
光线传感器(Ambient Light ) UIScreen、AVCaptureSession 自动调节屏幕亮度、手电筒的开启(mobile有)
距离传感器(Proximity) UIDevice 打电话自动锁屏
计步器(Pedometer) CMPedometer 运动数据测量
加速计(Accelerometer) CMMotionManager 计步器、摇一摇
陀螺仪(Gyroscope) CMMotionManager 游戏控制
磁力计(Magnetometer) CMMotionManager 磁力感应
湿度传感器(Moisture) 维修时检测设备进水与否
内部温度传感器(Internal Temperature)
SensorDemo

二. 光线强弱测量

苹果有相关的私有api提供光线检测,但不允许上架产品使用,所以间接地使用其它方式检测光线强弱,有兴趣的小伙伴可以去研究下<GraphicsServices/GraphicsServices.h>中的GSEventSetBacklightLevel()方法

  1. 检测屏幕亮度
    1. UIScreenBrightnessDidChangeNotification:光线变化通知
    2. [UIScreen mainScreen].brightness:获取屏幕亮度

    光线亮度调节
    1. 对手机自动亮度调节进行设置,设置-->显示与亮度-->允许自动亮度调节,当手机感受到外界光线亮度变化时,会自动调节屏幕亮度
    2. 手动调节屏幕亮度
    
  2. 摄像头检测
    "通过对摄像头捕获的视频流的参数进行分析,亮度的参数key是kCGImagePropertyExifBrightnessValue"

    // AVCaptureVideoDataOutputSampleBufferDelegate代理方法
    // 摄像头输出的视频流
    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
        // 获取视频流的相关参数
        CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);
        NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
        CFRelease(metadataDict);
        
        "exif中有个brightness参数值"
        NSDictionary *exifMetadata = [metadata[(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
        float brightnessValue = [exifMetadata[(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
    
        // 在UI上显示
        self.cameraBightnessLbl.text = [NSString stringWithFormat:@"%.2f",brightnessValue];
    }
    

顺便将摄像头捕获视频流的方法贴出来
- (AVCaptureSession *)initCaptureSession {
// 创建会话
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];
        }
        
        // 输出:视频数据
        "当有视频数据输出时,会调用上面的AVCaptureVideoDataOutputSampleBufferDelegate代理方法"
        AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
        [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
        if ([captureSession canAddOutput:output]) {
            [captureSession addOutput:output];
        }
        
        // 实时预览:展现给用户
        AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
        previewLayer.frame = self.lightVideoV.bounds;
        [self.lightVideoV.layer addSublayer:previewLayer];
    }

三. 距离传感器检测

    1. 距离传感器默认是关闭的,需手动开启
       [UIDevice currentDevice].proximityMonitoringEnabled = YES/NO;

    2. 传感器开启后,通过通知监听是否有物体靠近
        1. 通知名:UIDeviceProximityStateDidChangeNotification;
        2. 返回对象:notification.object会返回UIDevice对象;
        3. 判断:UIDevice对象的proximityState属性指示是否靠近用户,YES(靠近)/NO(离开)

    3. 代码
        - (IBAction)proximitySwitch:(UISwitch *)sender {
            // 传感器的开启与关闭
            [UIDevice currentDevice].proximityMonitoringEnabled = sender.on;    

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

            // 通知监听状态
            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? @"物体靠近" : @"物体离开"));
            }
        }

四. 计步器

ios7-ios8的时候使用CMStepCounter,ios8及以后使用CMPedometer,下面介绍的是CMPedometer

  1. 计步器能获取的数据
    1. startDate:开始时间段
    2. endDate:结束时间段
    3. numberOfSteps:步数
    4. distance:距离
    5. floorsAscended:爬楼数
    6. floorsDescended:下楼数
    7. ios9以后的系统才有
    1. currentPace:当前步速 in seconds per meter
    2. currentCadence:当前步频 in steps per second
    8. ios10的
    1. averageActivePace:平均活跃时间段的步速

  2. 使用

    1. "运动与健康"权限设置
      1. 使用计步器需添加权限NSMotionUsageDescription描述
      2. 第一次使用CMPedometer对象的时候系统自动会向用户请求"运动与健康"授权
      3. 授权判断:[CMSensorRecorder isAuthorizedForRecording];
      4. 没找到授权的方法:不过由于系统会自动授权(第2条),通过下面的方法也可以达到授权的效果,
      该方法在获取到用户的选择之后才会进行回调
      [self.pedometer queryPedometerDataFromDate:toDate:withHandler:]

    2. 获取数据
      1. CMPedometer中的对象方法
      // 获取某时间段的历史数据
      - (void)queryPedometerDataFromDate:(NSDate *)start toDate:(NSDate *)end withHandler:(CMPedometerHandler)handler;
      // 从某时间段开始实时获取数据,停止获取的时候需调用stopPedometerUpdates方法
      - (void)startPedometerUpdatesFromDate:(NSDate *)start withHandler:(CMPedometerHandler)handler;
      // 获取计步器的状态(暂停/记步),停止获取的时候需调用stopPedometerEventUpdates方法
      - (void)startPedometerEventUpdatesWithHandler:(CMPedometerEventHandler)handler NS_AVAILABLE(NA,10_0) __WATCHOS_AVAILABLE(3_0);
      // 停止更新数据
      - (void)stopPedometerUpdates;
      // 停止状态更新
      - (void)stopPedometerEventUpdates NS_AVAILABLE(NA,10_0);

       2. 主要的代码实现
           // 1. 初始化计步器self.pedometer
           - (CMPedometer *)pedometer {
                  if (!_pedometer) {
                      _pedometer = [[CMPedometer alloc] init];
                  }
                 return _pedometer;
             }
      
            // 2. 通过一个开关开启/关闭记步功能
           - (IBAction)recordStepCount:(UIButton *)sender {
                 BOOL start = !sender.selected;
                 // 开始记步
                 if(start){
      
                     // 可用性检测
                     if(![CMPedometer isStepCountingAvailable]){
                         [self showWithTitle:@"计步器不可用" message:nil];
                        return;
                     }
      
                     // 1 授权
                     // 1.1 pedometer第一次被使用时,会由系统主动提示用户授权“运动与健康”;但没找到授权的相关方法,通过该方式实现需求
                     __weak typeof (self) weakSelf = self;
                     [self.pedometer queryPedometerDataFromDate:[NSDate date] toDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
                         // 1.2 用户选择了授权与否之后,该block才会被调用,不在主线程
                         dispatch_async(dispatch_get_main_queue(), ^{
                             // 1.3 授权判断
                             if(![CMSensorRecorder isAuthorizedForRecording]){
                                 [weakSelf showWithTitle:@"未授权" message:@"前往设置->隐私->运动与健康,点击允许访问"];
                                 return;
                             }
                             sender.selected = YES;
                         
                             // 2. 获取数据
                             // 2.1 监测计步器状态:暂停、恢复
                             [weakSelf.pedometer startPedometerEventUpdatesWithHandler:^(CMPedometerEvent * _Nullable pedometerEvent, NSError * _Nullable error) {
                                 NSLog(@"%@",pedometerEvent.type==CMPedometerEventTypePause? @"暂停":@"继续");
                             }];
                         
                             // 2.2 监测计步器数据
                             [weakSelf.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
                                 if (pedometerData) {
                                     // 2.3 处理数据:回调不在主线程,所以需要回到主线程处理
                                     dispatch_async(dispatch_get_main_queue(), ^{
                                         // 数据存储在pedometerData中
                                         ...
                                     });
                                 }
                             }];
                         });
                     }];
                 } else {
                     // 结束记步
                     sender.selected = NO;
                     [self.pedometer stopPedometerUpdates];
                     [self.pedometer stopPedometerEventUpdates];
                 }
             }
      

四. 加速计/陀螺仪/磁力计

  1. 加速计/陀螺仪/磁力计这三种感应器使用也很简单,直接通过CMMotionManager对象处理,获取数据的方法大同小异;
  1. CMMotionManager对象可以检测设备的可用性、获取数据、设置数据的更新频率,有需要的可以直接去头文件看看
  2. 通过计步器制作摇一摇、计步器:按我理解呢,如果考虑周全,摇一摇、记步也需要比较复杂的算法,自己做难免不全面;并且这些苹果已经有对应API,直接调用就好
  3. CMMotion的使用可以参考这篇文章详说CMDeviceMotion

由于这三种感应器获取数据方式一致,就只对加速计进行举例说明

  1. 成员变量及方法

     // 存储加速计数据
    @property(readonly, nullable) CMAccelerometerData *accelerometerData;
    
    // 开始更新加速计数据,不带回调,可以添加定时器定时去获取CMMotionManager对象的accelerometerData数据
    - (void)startAccelerometerUpdates;
    
    // 开始更新加速计数据,带回调,由于数据可能更新频率快,不建议使用主队列
    - (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler;
    
    // 不再需要更新数据的时候需要调用停止更新的方法
    - (void)stopAccelerometerUpdates;
    
  2. 使用步骤
    // 1. 初始化CMMotionManager对象并设置属性存储,设置数据的更新间隔
    - (CMMotionManager *)motionManage {
    if (!_motionManage) {
    _motionManage = [[CMMotionManager alloc] init];
    // 控制传感器的更新间隔
    _motionManage.accelerometerUpdateInterval = 0.2;
    _motionManage.gyroUpdateInterval = 0.2;
    _motionManage.magnetometerUpdateInterval = 0.2;
    }
    return _motionManage;
    }

    // 2. 开始/结束更新数据,只举例带回调的方法
    - (IBAction)accelerometerTest:(UIButton *)sender {
        BOOL start = !sender.selected;
    
        // 2.1 根据设置的时间间隔定期更新数据
        if (start) {
            // 可用性检测
            if(![self.motionManage isAccelerometerAvailable]){
                [self showWithTitle:@"加速计不可用" message:nil];
                return;
            }
            sender.selected = YES;
            __weak typeof (self) weakSelf = self;
    
            // 数据更新有可能比较频繁,不建议使用主队列
            NSOperationQueue *queue = [NSOperationQueue new];
            [self.motionManage startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
                // 回到主线程
                dispatch_async(dispatch_get_main_queue(), ^{
                    // 数据显示
                    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 停止获取数据
            sender.selected = NO;
            [self.motionManage stopAccelerometerUpdates];
        }
    }
    
  3. 自带摇一摇功能
    UIResponder类中已经封装好了摇一摇功能,当对象成为第一响应者之后,
    系统就会通知对象摇一摇的开始/结束状态,实现以下方法就可以

    - (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event;
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容