传感器技术
加速计传感器(微信摇一摇,计步器)
目标: 演示加速剂的使用,做一个小球滚动的游戏.
关键词:Accelerrmoter
作用:检测设备在x,y,z的加速度(力的作用)
导入框架CoreMotion.
核心类:CMMotionmanager.动作管理者,包含加速度数据,加速度数据和磁场数据等.
逻辑代码部分
3.0. 判断硬件是否支持
3.1. 获取加速度数据.
3.2. 提供了两种获取方式
1.push(推送)实时向你推送.使用队列,通过block定时执行.
1. 判断是否是激活加速计状态.??pull需要么??
2.pull(拉取)在你需要数据的时候,主动获取.开启速度更新
2. 通过acceleremoterData属性,去获取数据.
小球是通过push.因为要实时更新.
============== 让小球滚起来. ===========
利用x,y的值,来给小球添加作用力(将0.5的力放大),竖直偏移是它向下移动,水平偏移是它左右移动.注意点生命周期问题:图层.改圆角,不能再viewDidload中改,也不能在viewWillAppear(还没布局),在ViewDidAppear也不行,会跳动先方后圆.最后知道了ViewDidLayoutSubviews中写.这个时候自动布局才已经改变了.
// 用于接收和处理`加速事件`和其它的动作事件
#import <CoreMotion/CoreMotion.h>
- (void)viewDidLoad {
[super viewDidLoad];
// ========== 1. 获取加速度的数据 ==========
// 1. 判断加速计是否可用
if (self.motionManager.accelerometerAvailable == NO) {
NSLog(@"不支持该硬件!");
return;
}
// ========== 2. 获取数据 (Pull方式) ==========
// 1.0 秒更新一次数据
self.motionManager.accelerometerUpdateInterval = 1.0;
// 1. 激活加速计 (提供数据)
// 开始更新新数据 (Pull方式)
[self.motionManager startAccelerometerUpdates];
// [self.motionManager stopAccelerometerUpdates];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// ========== 在点击时, 获取加速计的数据 ==========
// 通过一个属性来获取到最新的加速计数据
CMAccelerometerData *data = self.motionManager.accelerometerData;
// 加速计数据
CMAcceleration acce = data.acceleration;
NSLog(@"%f, %f, %f", acce.x, acce.y, acce.z);
}
#pragma mark - Getter & Setter
- (CMMotionManager *)motionManager
{
if (_motionManager == nil) {
_motionManager = [[CMMotionManager alloc] init];
}
return _motionManager;
}
#pragma mark - 小球案例
/*
在self.view 的 layoutSubviews 执行完成
当该方法执行时, 子视图已经布局完成
* 当view的frame值或者它的子视图的frame被修改, 需要重新布局
*/
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.imageView.layer.cornerRadius = self.imageView.bounds.size.width * 0.5;
// IB中也可以设置 clipToBounds
// self.imageView.clipsToBounds = YES;
// self.imageView.layer.masksToBounds = YES;
}
// 开始更新加速计数据 (Push, 定时更新)
[self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
if (error) {
NSLog(@"获取数据失败! %@", error);
return;
}
// CMAccelerometerData : 表示加速计的数据
// CMAcceleration : 表示加速度的结构体
CMAcceleration data = accelerometerData.acceleration;
NSLog(@"%f, %f", data.x, data.y);
// ========== 让小球滚动 ==========
// 1. 利用x, y的值, 来给小球添加作用力 (将0.5的力放大成10像素)
CGFloat speedX = data.x * 30;
CGFloat speedY = data.y * 30;
// NSLog(@"%f, %f", speedX, speedY);
// 2. 改变frame值, 让小球滚动
CGRect rect = self.imageView.frame;
rect.origin.x += speedX;
// y方向的数据要颠倒
rect.origin.y += -speedY;
[UIView animateWithDuration:0.25 animations:^{
self.imageView.frame = rect;
}];
NSLog(@"%@", NSStringFromCGRect(self.imageView.frame));
// 在非主线程当中更新UI界面, frame值会改变, 但是界面是没有影响(不确定)
}];
陀螺仪(赛车类游戏)
目标:掌握陀螺仪的使用
* 获取角速度
关键类:CMMotionManager,关键字:Groy
- 同加速度前面部分代码.
- push获取数据
2.1. 判断是否激活,激活
2.2. 开始陀螺仪数据更新
2.3. 获取旋转速率(角速度),x,y,z这里面代表三个方向旋转的角速度数据. - pull方式(类比上面)
代码部分都类似距离传感器
[self.motionManager startGyroUpdates];
[self.motionManager startGyroUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMGyroData * _Nullable gyroData, NSError * _Nullable error) {
// CMGyroData : 表示陀螺仪的数据
// CMRotationRate : 表示旋转速率, 角速度的结构体,
CMRotationRate rate = gyroData.rotationRate;
// 静止状态下, 值是接近于0的
NSLog(@"%f, %f, %f", rate.x, rate.y, rate.z);
}];
计步器来统计数据
关键类:
CMStepCounter早期使用的,
CMPedometer取代前面的检测走了多少步.
类似上面,也是通过计步数据对象,的各个属性获取相关值.
#import <CoreMotion/CoreMotion.h>
// 1. 实例化一个计步器
self.pedometer = [[CMPedometer alloc] init];
// 开始统计相关的行走信息 (开始统计的时间)
// 会定时的触发Block方法, 没办法设置
[self.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
// CMPedometerData: 表示计步器所统计的数据
// ========== 检测走了多少步 ==========
// 1. 判断该检测是否可用 (依赖硬件)
if ([CMPedometer isStepCountingAvailable] == NO) {
NSLog(@"不支持步数的计算统计");
return;
} else {
// 走了几步
NSNumber *number = pedometerData.numberOfSteps;
NSLog(@"走了%zd步", number.intValue);
}
// ========== 检测走了多少米 ==========
if ([CMPedometer isDistanceAvailable] == YES) {
NSNumber *distance = pedometerData.distance;
NSLog(@"走了%f米", distance.floatValue);
}
}];
距离传感器(靠近手机,打电话)
相关框架:CoreMotion(动作)
观察相关类,没有发现距离传感器的类(特别没有专门的类),是通过UIDevice来获取的.
关键字proximity距离.
UIDevice可以用来判断系统版本.
-
[UIDevice currentDevice].proximityMonitoring
默认关闭的 - 不是所有的设备都有距离传感器的.判断方法特别.
Demo目标:类似微信的语音播放,扬声器和听筒切换
思路: - 距离传感器判断是否靠近屏幕.
- 根据状态,修改声音的输出.
知识点: UIDevice类内,有距离传感器状态改变的通知
代码:
- 开启距离传感器
- 添加接收系统通知
- 播放声音改变,关键类:AVAudioSession(音频会话),是个单例,有个重写输出音频端口的方法.
- 猜想根据通知参数的userInfo,获取当前的距离传感器状态,但是打印发现没有数据,只是通知你改变了状态了.
- 观看文档发现直接通过获取设备的proximityState属性,来判断是否靠近设备.
前提是需要向系统声明我所需要使用的音频相关功能.只需要走一次
- AVAudioSession单例有个setCategory方法进行类别的设置,参数是你需要做的功能.
- 修改声音的输出端口,是听筒还是扬声器.
- 播放音乐AVAudioPlayer 播放音乐的类,1.实例化,2.调用播放方法.
代码部分
导入
#import <AVFoundation/AVFoundation.h>
// ========== 1. 开启距离传感器 ==========
if ([[UIDevice currentDevice] isProximityMonitoringEnabled] == NO) {
NSLog(@"当前距离传感器没有打开");
// 尝试打开距离传感器, 如果失败了, 意味着不支持该硬件
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
// 打开结果判断 (是否支持硬件)
if ([[UIDevice currentDevice] isProximityMonitoringEnabled] == NO) {
NSLog(@"不支持该硬件, 无法使用");
} else {
NSLog(@"成功打开距离传感器");
// ========== 2. 监听通知 ==========
// 监听通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityDidChangeAction:) name:UIDeviceProximityStateDidChangeNotification object:nil];
}
}
// ========== 2. 向系统声明, 所使用的音频的相关功能 ==========
// 告诉系统, 本应用会使用哪一些与音频相关的功能
NSError *error;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if (error) {
NSLog(@"音频功能声音出错: %@", error);
}
// ========== 3. 测试: 播放歌曲 ==========
NSURL *url = [[NSBundle mainBundle] URLForResource:@"南征北战 - 我的天坑.mp3" withExtension:nil];
// 实例化音乐播放器
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (error) {
NSLog(@"歌曲播放出错");
}
// 开始播放
[self.player play];
- (void)proximityDidChangeAction:(NSNotification *)notification
{
// ========== 1. 判断是否靠近屏幕 ==========
// 并没有通过userInfo来携带数据, 应该直接获取距离传感器的proximityState属性
NSLog(@"%@", notification.userInfo);
/** 音频会话
1. 设置应用当前所处的音频环境 (播放声音, 录音, 用什么来播放)
2. 向系统诠释应用使用音频相关行为的意图
*/
// 设置音频的输出设备 (听筒或扬声器)
/**
AVAudioSessionPortOverrideNone = 0, (扬声器)
AVAudioSessionPortOverrideSpeaker (听筒)
*/
AVAudioSessionPortOverride port = [UIDevice currentDevice].proximityState == NO ? AVAudioSessionPortOverrideSpeaker : AVAudioSessionPortOverrideNone;
NSError *error;
[[AVAudioSession sharedInstance] overrideOutputAudioPort:port error:&error];
if (error) {
NSLog(@"改变声音输出失败");
}
}
- (void)dealloc
{
// 移除观察者
[[NSNotificationCenter defaultCenter] removeObserver:self];
}