美文网首页iOS 知识收集
IOS + 传感器的使用(加速计、陀螺仪、计步器等)

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

作者: YaoYaoX | 来源:发表于2017-05-11 16:26 被阅读2714次

    一. 简介

    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;
      

    相关文章

      网友评论

        本文标题:IOS + 传感器的使用(加速计、陀螺仪、计步器等)

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