一. 简介
iOS设备内置了一些传感器,并提供了相关的API供调用,关于iOS传感器的类型及作用等相关知识,可以查看文章iOS开发之传感器 ,本篇文章的demo的github地址是:传感器的使用,感兴趣的可以瞄瞄。
传感器 | 涉及的类 | 作用 |
---|---|---|
光线传感器(Ambient Light ) | UIScreen、AVCaptureSession | 自动调节屏幕亮度、手电筒的开启(mobile有) |
距离传感器(Proximity) | UIDevice | 打电话自动锁屏 |
计步器(Pedometer) | CMPedometer | 运动数据测量 |
加速计(Accelerometer) | CMMotionManager | 计步器、摇一摇 |
陀螺仪(Gyroscope) | CMMotionManager | 游戏控制 |
磁力计(Magnetometer) | CMMotionManager | 磁力感应 |
湿度传感器(Moisture) | 维修时检测设备进水与否 | |
内部温度传感器(Internal Temperature) |
二. 光线强弱测量
苹果有相关的私有api提供光线检测,但不允许上架产品使用,所以间接地使用其它方式检测光线强弱,有兴趣的小伙伴可以去研究下<GraphicsServices/GraphicsServices.h>中的GSEventSetBacklightLevel()方法
-
检测屏幕亮度
1. UIScreenBrightnessDidChangeNotification:光线变化通知
2. [UIScreen mainScreen].brightness:获取屏幕亮度光线亮度调节 1. 对手机自动亮度调节进行设置,设置-->显示与亮度-->允许自动亮度调节,当手机感受到外界光线亮度变化时,会自动调节屏幕亮度 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. 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:平均活跃时间段的步速-
使用
"运动与健康"权限设置
1. 使用计步器需添加权限NSMotionUsageDescription描述
2. 第一次使用CMPedometer对象的时候系统自动会向用户请求"运动与健康"授权
3. 授权判断:[CMSensorRecorder isAuthorizedForRecording];
4. 没找到授权的方法:不过由于系统会自动授权(第2条),通过下面的方法也可以达到授权的效果,
该方法在获取到用户的选择之后才会进行回调
[self.pedometer queryPedometerDataFromDate:toDate:withHandler:]-
获取数据
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]; } }
四. 加速计/陀螺仪/磁力计
- 加速计/陀螺仪/磁力计这三种感应器使用也很简单,直接通过CMMotionManager对象处理,获取数据的方法大同小异;
- CMMotionManager对象可以检测设备的可用性、获取数据、设置数据的更新频率,有需要的可以直接去头文件看看
- 通过计步器制作摇一摇、计步器:按我理解呢,如果考虑周全,摇一摇、记步也需要比较复杂的算法,自己做难免不全面;并且这些苹果已经有对应API,直接调用就好
- CMMotion的使用可以参考这篇文章详说CMDeviceMotion
由于这三种感应器获取数据方式一致,就只对加速计进行举例说明
-
成员变量及方法
// 存储加速计数据 @property(readonly, nullable) CMAccelerometerData *accelerometerData; // 开始更新加速计数据,不带回调,可以添加定时器定时去获取CMMotionManager对象的accelerometerData数据 - (void)startAccelerometerUpdates; // 开始更新加速计数据,带回调,由于数据可能更新频率快,不建议使用主队列 - (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler; // 不再需要更新数据的时候需要调用停止更新的方法 - (void)stopAccelerometerUpdates;
-
使用步骤
// 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]; } }
-
自带摇一摇功能
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;