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)
IOS + 传感器的使用(加速计、陀螺仪、计步器等)_第1张图片
SensorDemo

二. 光线强弱测量

苹果有相关的私有api提供光线检测,但不允许上架产品使用,所以间接地使用其它方式检测光线强弱,有兴趣的小伙伴可以去研究下中的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;
    

你可能感兴趣的:(IOS + 传感器的使用(加速计、陀螺仪、计步器等))