简介:CoreMotion是iOS系统目前用于处理加速度计,陀螺仪,计步器和环境相关事件。 Core Motion的报告来自iOS设备的板载硬件的运动和环境相关数据,包括加速度计和陀螺仪,以及计步器,磁力计和气压计。您可以使用此框架访问硬件生成的数据,以便在应用程序中使用它。例如,游戏可能使用加速度计和陀螺仪数据来控制屏幕上的游戏行为。
提示:
在iOS 10.0上或之后链接的iOS应用程序必须在其文件中包含用于所需数据类型的使用说明密钥。未能包含这些密钥将导致应用程序崩溃。要具体访问运动和健身数据,必须包含NSMotionUsageDescription,可以在Info.plist中添加字段来妥善解决。
在进行CoreMotion的具体操作之前我们需要详细的了解一下Api的构成
简介:CMMotionManager用于启动和管理运动服务的对象。 使用对象启动报告设备的板载传感器检测到的移动的服务。使用此对象可以接收四种类型的运动数据:CMMotionManager
设备运动数据,指示与运动相关的关键属性,例如设备的用户启动的加速度,其姿态,旋转速率,相对于校准磁场的方向以及相对于重力的方向。这些数据由Core Motion的传感器融合算法提供。经处理的设备运动数据给出设备的姿态,旋转速率,校准磁场,重力方向以及用户赋予设备的加速度。
提示:
在一般情况下,一个App仅可以创建一个CMMotionManager对象,因为此类的多个实例化之后,可能会影响从加速度计和陀螺仪接收数据的速率。
可以在指定的更新间隔接收实时传感器数据,或者让传感器收集数据并将其存储起来以供以后检索。对于这两种方法,当不再需要数据时,调用适当的停止方法(stopAccelerometerUpdates, stopgyroupdate, stopMagnetometerUpdates,和stopDeviceMotionUpdates)。
为了以特定的时间间隔接收运动数据,应用程序调用一个“start”方法,该方法接受一个操作队列(NSOperationQueue的实例)和一个特定类型的块处理程序来处理这些更新。运动数据被传递到块处理程序。更新频率由“interval”属性的值决定。
Accelerometer加速度计使用方式:
Gyroscope陀螺仪使用方式:
Magnetometer磁强计使用方式:
Device motion设备运动使用方式:
为了通过定期采样处理运动数据,app调用不带参数的“start”方法,并定期访问属性为给定类型的运动数据保存的运动数据。这种方法是游戏等应用程序的推荐方法。处理块中的加速度计数据会带来额外的开销,并且大多数游戏应用程序在渲染帧时仅对最新的运动数据样本感兴趣。
加速度计:调用startAccelerometerUpdates来开始更新,并通过读取accelerometerData属性定期访问CMAccelerometerData对象。
陀螺仪:调用startgyroupdate开始更新,并通过读取gyroData属性定期访问CMGyroData对象。
磁强计:调用startMagnetometerUpdates来开始更新,并通过读取magnetometerData属性定期访问CMMagnetometerData对象。
设备运动:调用startDeviceMotionUpdatesUsingReferenceFrame:或startdedevicemotionupdates方法开始更新,并通过读取deviceMotion属性定期访问CMDeviceMotion对象。
检查服务的可用性:
属性 | 类型 | 作用 |
---|---|---|
accelerometerAvailable | BOOL | 设备上是否有加速度计 |
gyroAvailable | BOOL | 设备上是否有陀螺仪 |
magnetometerAvailable | BOOL | 设备上是否有磁强计 |
deviceMotionAvailable | BOOL | 动作服务在设备上是否可用 |
检查功能的活跃状态:
属性 | 类型 | 作用 |
---|---|---|
accelerometerActive | BOOL | 当前是否正在进行加速度计更新 |
gyroActive | BOOL | 确定当前是否正在进行陀螺仪更新 |
magnetometerActive | BOOL | 确定当前是否正在进行磁力计更新 |
deviceMotionActive | BOOL | 确定应用程序是否从设备动作服务接收更新 |
使用样例如下:
#import "ViewController.h"
#import
@interface ViewController ()<UIAccelerometerDelegate>{
CMMotionManager * motionManager;
}
@property(nonatomic,strong)UIDynamicAnimator *animator;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建一个管理者对象
motionManager = [[CMMotionManager alloc] init];
// 判断设备是否支持
[self isActived];
}
// 判断设备是否支持
- (void)isActived{
// 判断设备是否支持加速度计
if(motionManager.accelerometerAvailable){
[motionManager startAccelerometerUpdates];
NSLog(@"该设备支持加速度计");
}else{
NSLog(@"该设备不支取加速度");
}
// 判断设备是否支持陀螺仪
if (motionManager.gyroAvailable){
[motionManager startGyroUpdates];
NSLog(@"该设备支持陀螺仪");
}else{
NSLog(@"该设备不支持陀螺仪");
}
// 判断设备是否支持磁强计
if (motionManager.magnetometerAvailable){
[motionManager startMagnetometerUpdates];
NSLog(@"该设备支持磁强计");
}else{
NSLog(@"该设备不支持磁强计");
}
// 判断设备是否支持DeviceMotion
if (motionManager.deviceMotionAvailable) {
[motionManager startDeviceMotionUpdates];
NSLog(@"该设备支持DeviceMotion");
}else{
NSLog(@"该设备不支持DeviceMotion");
}
}
@end
运行结果如下:
在做CoreMotion的测试的时候,我们必须使用真机来进行测试,模拟器没法实现。接下来我们继续详细讲解。
加速度计的使用,首先要用我们的管理者启用服务:
[motionManager startAccelerometerUpdates];
Tips:在这里需要注意一点,在CoreMotion管理的iOS硬件的Api都有两种方式来获取数据
官方定义为:pull(主动获取)、push(通过代码块获取)
接下来的代码中两种方式都使用
在启用服务之后,我们就来主动获取数据(pull),代码如下:
// 主动获取加速度数据
- (void)useAccelerometerPull{
CMAccelerometerData* accelerometerData = motionManager.accelerometerData;
NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
}
这只是主动去获取一次在当前时间的加速度计的值,如果我们需要一直获取,那就使用定时器,在成员变量中声明一个showTimer,使用showTimer调用useAccelerometerPull,来实现主动获取数据,代码如下:
showTimer = [NSTimer timerWithTimeInterval:0.01f target:self selector:@selector(useAccelerometerPull) userInfo:nil repeats:YES];
我们也可以让CoreMotion自带的块方法来获取数据,代码如下,详细内容在注释里面:
// 通过块方法回调加速度数据
- (void)useAccelerometerPush{
// 设定获取间隔时间
motionManager.accelerometerUpdateInterval = 0.01f;
// 添加线程(如果要刷新UI,就使用主线程)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 块方法回调
[motionManager startAccelerometerUpdatesToQueue:queue
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
// 打印数据
NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
}];
}
运行效果:
陀螺仪的使用,首先要用我们的管理者启用服务:
[motionManager startGyroUpdates];
和加速度计一样,使用两个方法获取数据:
// 主动获取陀螺仪数据
- (void)useGyroAvailablePull{
CMGyroData* gyroData = motionManager.gyroData;
NSLog(@"陀螺仪X = %.04f",gyroData.rotationRate.x);
NSLog(@"陀螺仪Y = %.04f",gyroData.rotationRate.y);
NSLog(@"陀螺仪Z = %.04f",gyroData.rotationRate.z);
}
// 通过块方法回调陀螺仪数据
- (void)useGyroAvailablePush{
// 设定获取间隔时间
motionManager.gyroUpdateInterval = 0.1f;
// 添加线程
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
// 块方法回调
[motionManager startGyroUpdatesToQueue:queue withHandler:^(CMGyroData * _Nullable gyroData, NSError * _Nullable error) {
// 打印数据
NSLog(@"陀螺仪X = %.04f",gyroData.rotationRate.x);
NSLog(@"陀螺仪Y = %.04f",gyroData.rotationRate.y);
NSLog(@"陀螺仪Z = %.04f",gyroData.rotationRate.z);
}];
}
磁强计的使用,首先要用我们的管理者启用服务:
[motionManager startMagnetometerUpdates];
然后依旧是两种方式获取数据,代码如下:
// 主动获取磁强计数据
- (void)useMagnetometerPull{
CMMagnetometerData* magnetometerData = motionManager.magnetometerData;
NSLog(@"磁强计X = %.04f",magnetometerData.magneticField.x);
NSLog(@"磁强计Y = %.04f",magnetometerData.magneticField.y);
NSLog(@"磁强计Z = %.04f",magnetometerData.magneticField.z);
}
// 通过块方法回调磁强计数据
- (void)useMagnetometerPush{
// 设定获取间隔时间
motionManager.magnetometerUpdateInterval = 0.1f;
// 添加线程
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
// 块方法回调
[motionManager startMagnetometerUpdatesToQueue:queue withHandler:^(CMMagnetometerData * _Nullable magnetometerData, NSError * _Nullable error) {
NSLog(@"磁强计X = %.04f",magnetometerData.magneticField.x);
NSLog(@"磁强计Y = %.04f",magnetometerData.magneticField.y);
NSLog(@"磁强计Z = %.04f",magnetometerData.magneticField.z);
}];
}
运行效果:
看到这里大家别着急啊,关于CoreMotion的东西马上讲解完毕,毕竟是科普和分享知识,细致认真才是最重要的。一会儿会给大家看看我做的demo的效果。
在设备运动这里,我查了一下资料,一般所需要的功能就是获取自身与水平的夹角,以及自身旋转角度,在代码里我做了介绍。
// 启用服务
[motionManager startDeviceMotionUpdates];
// 主动获取deviceMotion数据
- (void)useDeviceMotionUpdatePull {
[motionManager startDeviceMotionUpdates];
CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
double gravityX = deviceMotion.gravity.x;
double gravityY = deviceMotion.gravity.y;
double gravityZ = deviceMotion.gravity.z;
NSLog(@"\n重力:\nX:%f\nY:%f\nZ:%f", gravityX, gravityY, gravityZ);
}
// 通过块方法回调deviceMotion数据
- (void)useDeviceMotionUpdatePush{
// 设定获取间隔时间
motionManager.deviceMotionUpdateInterval = 0.1f;
// 添加线程
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
[motionManager startDeviceMotionUpdatesToQueue:queue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
// 重力加速度在各个方向的分量
double gX = motion.gravity.x;
double gY = motion.gravity.y;
double gZ = motion.gravity.z;
NSLog(@"重力X:%f -- Y:%f -- Z:%f", gX, gY, gZ);
// 获取手机的倾斜角度(zAngle是手机与水平面的夹角, xyAngle是手机绕自身旋转的角度):
double zAngle = atan2(gZ,sqrtf(gX*gX+gY*gY))/M_PI*180.0;
double xAngle = atan2(gX,grY)/M_PI*180.0;
NSLog(@"与水平夹角: %f----自身旋转角度:%f", zAngle, xyAngle);
}];
}
运行效果:
在这里引述一下函数:ATAN2和sqrtf
ATAN2:
C 语言里 double atan2(double y,double x) 返回的是原点至点(x,y)的方位角,即与 x 轴的夹角。也可以理解为复数 x+yi 的辐角。返回值的单位为弧度,取值范围为(-π,π];
Excel 里 ATAN2(x,y)返回的是原点至点(x,y)的方位角。返回值的单位为弧度,取值范围为(-π,π]。
注意:
1、C 函数与 Excel 函数的参数顺序正好相反;
2、C 函数允许 x、y 同时为零,Excel 不允许 x、y 同时为零。
与 atan 的不同,atan2 比 atan 稳定。
如:atan(y/x),当 y 远远大于 x 时,计算结果是不稳定的。
atan2(y,x)的做法:当 x 的绝对值比 y 的绝对值大时使用 atan(y/x);反之使用 atan(x/y)。这样就保证了数值稳定性。sqrtf:
用来计算参数的平方根
好了,关于这四个设备动作就讲到这里,大家可以去coding一下,练练手。接下来继续讲解两个例子
其实代码很简单,主要是根据acceleration值来判断手机是否晃动。代码如下:
- (void)isShake{
motionManager.deviceMotionUpdateInterval = 0.01f;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *data, NSError *error{
double x = accelData.acceleration.x;
double y = accelData.acceleration.y;
double z = accelData.acceleration.z;
if (fabs(x)>2.0 || fabs(y)>2.0 ||fabs(z)>2.0) {
NSLog(@"检测到晃动");
}
}];
}
如果是带有方向的晃动,那就是这样:
- (void)shake{
motionManager.deviceMotionUpdateInterval = 0.01f;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *data, NSError *error) {
if (data.userAcceleration.x < -1.5f) {
// 往左晃动
}
if (data.userAcceleration.x > 1.5f) {
// 往右晃动
}
if (data.userAcceleration.y < -1.5f) {
// 往上晃动
}
if (data.userAcceleration.y > 1.5f) {
// 往下晃动
}
}];
}
演示效果如图:
然后我们再来拓展一个功能:平衡球。大家都玩过平衡球游戏吧?什么?没有?那我给大家做出来看看。
直接上代码:
- (void)viewDidLoad {
[super viewDidLoad];
motionManager = [[CMMotionManager alloc] init];
// 启用服务
[motionManager startAccelerometerUpdates];
self.view.backgroundColor = [UIColor whiteColor];
// 创建视图
[self createUI];
// 添加加速度测试
[self useAccelerometerPush];
}
-(void)createUI{
yuan = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
yuan.center = self.view.center;
yuan.layer.cornerRadius = 40;
yuan.backgroundColor = [UIColor redColor];
[self.view addSubview:yuan];
}
// 通过块方法回调加速度数据
- (void)useAccelerometerPush{
// 设定获取间隔时间
motionManager.accelerometerUpdateInterval = 0.01f;
// 块方法回调(刷新视图,必须在主线程)
[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
// 获得的加速度要考虑到加速度传感器的原点是物理重心,而不是屏幕右上角
// x轴方向的速度加上x轴方向获得的加速度
self->speedX += accelerometerData.acceleration.x;
// y轴方向的速度加上y轴方向获得的加速度
self->speedY += accelerometerData.acceleration.y;
// 小球将要移动到的x轴坐标
CGFloat posX = self->yuan.center.x + self->speedX;
// 小球将要移动到的y轴坐标
CGFloat posY = self->yuan.center.y - self->speedY;
// 在碰到屏幕边缘之后反弹
if (posX < 0.0) {
posX = 0.0;
// 在碰到屏幕左边以0.1倍的速度反弹
self->speedX *= -0.1;
}else if(posX > self.view.bounds.size.width){
posX = self.view.bounds.size.width;
// 在碰到屏幕右边以0.1倍的速度反弹
self->speedX *= -0.1;
}
if (posY < 0.0) {
posY = 0.0;
// 碰到屏幕顶部不反弹
self->speedY = 0.0;
}else if (posY > self.view.bounds.size.height){
posY = self.view.bounds.size.height;
// 碰到屏幕底部以0.1倍的速度反弹
self->speedY *= -0.1;
}
// 小球视图的位置改变
self->yuan.center = CGPointMake(posX, posY);
// 打印数据
NSLog(@"加速度X = %.04f",accelerometerData.acceleration.x);
NSLog(@"加速度Y = %.04f",accelerometerData.acceleration.y);
NSLog(@"加速度Z = %.04f",accelerometerData.acceleration.z);
}];
}
演示效果如下: