面试被问到了硬件,面试官具体指出陀螺仪,就简单研究下
首先必须真机测试,模拟器无效
应用场景:
1、指南针方向
2、计步数
3、摇一摇功能
加速器:检测设备在X、Y、Z轴上的加速度 (哪个方向有力的作用,哪个方向运动了)根据加速度数值,就可以判断出在各个方向上的作用力度
源于苹果的运动框架CoreMotion.framework
它不仅仅提供给你获得实时的加速度值和旋转速度值,更重要的是,苹果在其中集成了很多算法,
可以直接给你输出把重力加速度分量剥离的加速度,省去你的高通滤波操作,以及提供给你一个专
门的设备的三维位置信息。
在CoreMotion中有2种获取数据方式:
Push方式:
提供一个线程管理器NSOperationQueue和一个回调Block,CoreMotion自动在每一个采样数据到来的时候回调这个Block,进行处理。在这种情况下,Block中的操作会在你自己的主线程内执行。
Pull方式:
你必须主动去向CMMotionManager要数据,这个数据就是最近一次的采样数据。你不去要,CMMotionManager就不会给你
获取重力方向
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *arrowImageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 100, 200, 300)];
arrowImageView.image = [UIImage imageNamed:@"arrow"];
[self.view addSubview:arrowImageView];
//创建运动管理者对象
CMMotionManager *motionManager = [[CMMotionManager alloc]init];
if (!motionManager.accelerometerAvailable) { // 判断 加速器是否可用
return;
}
// motionManager.accelerometerUpdateInterval = 1/30.0; // 1 秒钟采样30次
motionManager.accelerometerUpdateInterval = 2;
//开始更新,后台线程开始运行。这是Pull方式。
// [motionManager startAccelerometerUpdates];
// push方法 加速度传感器开始采样,每次采样结果在block中返回
[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error)
{
//获取并处理加速度计数据
CMAccelerometerData *newestAccel = motionManager.accelerometerData;
double accelerationX = newestAccel.acceleration.x;
double accelerationY = newestAccel.acceleration.y;
// 返回值的单位为弧度,atan2(x,y)返回的是点(x,y)与x轴的夹角
double ra = atan2(-accelerationY, accelerationX);
arrowImageView.transform = CGAffineTransformMakeRotation(ra + M_PI_2);
}];
}
这里获取间隔数值尽量调大,节约资源
计步器
有两种一种是调用iOS系统中的健康数据但不是实时的,这里主要讲一下实时获取步数的
在iOS8及其以上系统中我们可以使用CMPedometer类获取行走相关信息(步数、距离、上楼、下楼、当前速度等)
常用方法
当前设备是否可以获取步数
+ (BOOL)isStepCountingAvailable;
当前设备是否可以获取距离
+ (BOOL)isDistanceAvailable;
当前设备是否可以获取上下楼层数
+ (BOOL)isFloorCountingAvailable;
当前设备是否可以获取速度(s/m)
+ (BOOL)isPaceAvailable
当前设备是否可以获取节奏
+ (BOOL)isCadenceAvailable
根据开始和结束时间查询行走相关信息
- (void)queryPedometerDataFromDate:(NSDate )start toDate:(NSDate )end withHandler:(CMPedometerHandler)handler;
从某一时刻开始监测步数变化
- (void)startPedometerUpdatesFromDate:(NSDate *)start withHandler:(CMPedometerHandler)handler;
停止监测步数变化
- (void)stopPedometerUpdates;
先写一个model
计步用
@property(nonatomic,assign) int step;
//g是一个震动幅度的系数,通过一定的判断条件来判断是否计做一步
@property(nonatomic,assign) double g;
把 CMMotionManager 写成单例
.h
.m 注意单例的写法
#import "StepManager.h"
#import "StepModel.h"
#import
#import
// 计步器开始计步时间(秒)
#define ACCELERO_START_TIME 2
// 计步器开始计步步数(步)
#define ACCELERO_START_STEP 1
@interface StepManager ()
{
NSMutableArray *arrAll; // 加速度传感器采集的原始数组
}
@property (nonatomic) NSInteger startStep; // 计步器开始步数
@property (nonatomic, retain) NSMutableArray *arrSteps; // 步数数组
@property (nonatomic, retain) NSMutableArray *arrStepsSave; // 数据库纪录步数数组
@end
@implementation StepManager
static StepManager *sharedManager;
static CMMotionManager *motionManager;
+ (StepManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sharedManager == nil) {
sharedManager = [[super allocWithZone:NULL] init];
}
motionManager = [[CMMotionManager alloc]init];
});
return sharedManager;
}
//开始计步
- (void)startWithStep
{
if (!motionManager.isAccelerometerAvailable) {
NSLog(@"加速度传感器不可用");
return;
}else {
motionManager.accelerometerUpdateInterval = 1.0/40;
}
[self startAccelerometer];
}
- (void)startAccelerometer
{
//如果不支持陀螺仪,需要用加速传感器来采集数据
if (!motionManager.isAccelerometerActive) {// isAccelerometerAvailable方法用来查看加速度器的状态:是否Active(启动)。
// 加速度传感器采集的原始数组
if (arrAll == nil) {
arrAll = [[NSMutableArray alloc] init];
}
else {
[arrAll removeAllObjects];
}
/*
1.push方式
这种方式,是实时获取到Accelerometer的数据,并且用相应的队列来显示。即主动获取加速计的数据。
*/
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error){
if (!motionManager.isAccelerometerActive) {
return;
}
//三个方向加速度值
double x = accelerometerData.acceleration.x;
double y = accelerometerData.acceleration.y;
double z = accelerometerData.acceleration.z;
//g是一个double值 ,根据它的大小来判断是否计为1步.
double g = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) - 1;
//将信息保存在步数模型中
StepModel *stepsAll = [[StepModel alloc] init];
stepsAll.g = g;
// 加速度传感器采集的原始数组
[arrAll addObject:stepsAll];
// 每采集10条,大约1.2秒的数据时,进行分析
if (arrAll.count == 10) {
// 步数缓存数组
NSMutableArray *arrBuffer = [[NSMutableArray alloc] init];
arrBuffer = [arrAll copy];
[arrAll removeAllObjects];
// 踩点数组
NSMutableArray *arrCaiDian = [[NSMutableArray alloc] init];
//遍历步数缓存数组
for (int i = 1; i < arrBuffer.count - 2; i++) {
//如果数组个数大于3,继续,否则跳出循环,用连续的三个点,要判断其振幅是否一样,如果一样,然并卵
if (![arrBuffer objectAtIndex:i-1] || ![arrBuffer objectAtIndex:i] || ![arrBuffer objectAtIndex:i+1])
{
continue;
}
StepModel *bufferPrevious = (StepModel *)[arrBuffer objectAtIndex:i-1];
StepModel *bufferCurrent = (StepModel *)[arrBuffer objectAtIndex:i];
StepModel *bufferNext = (StepModel *)[arrBuffer objectAtIndex:i+1];
//控制震动幅度,,,,,,根据震动幅度让其加入踩点数组,
if (bufferCurrent.g < -0.12 && bufferCurrent.g < bufferPrevious.g && bufferCurrent.g < bufferNext.g) {
[arrCaiDian addObject:bufferCurrent];
}
}
//如果没有步数数组,初始化
if (nil == self.arrSteps) {
self.arrSteps = [[NSMutableArray alloc] init];
self.arrStepsSave = [[NSMutableArray alloc] init];
}
// 踩点过滤
for (int j = 0; j < arrCaiDian.count; j++) {
StepModel *caidianCurrent = (StepModel *)[arrCaiDian objectAtIndex:j];
//如果之前的步数为0,则重新开始记录
if (self.arrSteps.count == 0) {
self.step = 0;
caidianCurrent.step = (int)self.step;
[self.arrSteps addObject:caidianCurrent];
[self.arrStepsSave addObject:caidianCurrent];
}
else {
if (self.startStep < ACCELERO_START_STEP) {//计步器开始计步步数 (步)
self.startStep ++;
break;
}
else if (self.startStep == ACCELERO_START_STEP) {
self.startStep ++;
// 计步器开始步数
// 运动步数(总计)
self.step = self.step + self.startStep;
}
else {
self.step ++;
}
}
}
}
}];
}
}
// 为了严谨,也要重写copyWithZone 和 mutableCopyWithZone
- (id)copyWithZone:(NSZone *)zone
{
return [StepManager sharedManager];
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [StepManager sharedManager];
}
+ (id) allocWithZone:(struct _NSZone *)zone
{
return [StepManager sharedManager] ;
}
@end
使用页面调用
摇一摇
iOS 系统在 UIResponder 类中提供了一个摇一摇的实现方法,大体实现思路为: 想让哪个 VC 实现摇一摇方案,将该 VC 变为第一响应者即可
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
// 支持摇一摇 此处应该写在AppDelegate中,为粘贴代码方便写在这里
[[UIApplication sharedApplication] setApplicationSupportsShakeToEdit:YES];
// 让此控制器成为第一响应者
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self resignFirstResponder];
}
// 开始摇动
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"开始摇动");
return;
}
// 取消摇动 如来电话
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"取消摇动");
return;
}
// 摇动结束
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake) { // 判断是否是摇动结束
SecondViewController *second = [SecondViewController new];
[self.navigationController pushViewController:second animated:YES];
NSLog(@"摇动结束");
}
return;
}
这是最简单的使用方法,但既然讲到加速器就用加速器在实现
加速器实现摇一摇
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
self.motionManager = [[CMMotionManager alloc] init];//一般在viewDidLoad中进行
self.motionManager.accelerometerUpdateInterval = 1.5;//加速仪更新频率,以秒为单位
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self startAccelerometer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveNotification:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveNotification:)
name:UIApplicationWillEnterForegroundNotification object:nil];
}
- (void)startAccelerometer
{
//以push的方式更新并在block中接收加速度
[self.motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc]init]
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
[self outputAccelertionData:accelerometerData.acceleration];
if (error) {
NSLog(@"motion error:%@",error);
}
}];
}
- (void)outputAccelertionData:(CMAcceleration)acceleration
{
//综合3个方向的加速度
double accelerameter =sqrt( pow( acceleration.x , 2 ) + pow( acceleration.y , 2 )
+ pow( acceleration.z , 2) );
//当综合加速度大于2.3时,就激活效果(此数值根据需求可以调整,数据越小,用户摇动的动作就越小,越容易激活,反之加大难度,但不容易误触发)
if (accelerameter>2.3f) {
//立即停止更新加速仪(很重要!)
[self.motionManager stopAccelerometerUpdates];
__weak typeof(self) WeakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
//UI线程必须在此block内执行,例如摇一摇动画、UIAlertView之类
// 第二个页面
SecondViewController *second = [SecondViewController new];
[WeakSelf.navigationController pushViewController:second animated:YES];
});
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
//停止加速仪更新(很重要!)
//当App退到后台时必须停止加速仪更新,回到当前时重新执行。否则应用在退到后台依然会接收加速度更新,可能会与其它当前应用冲突
[self.motionManager stopAccelerometerUpdates];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIApplicationWillEnterForegroundNotification object:nil];
}
//对应上面的通知中心回调的消息接收
- (void)receiveNotification:(NSNotification *)notification
{
if ([notification.name
isEqualToString:UIApplicationDidEnterBackgroundNotification])
{
[self.motionManager stopAccelerometerUpdates];
}else{
[self startAccelerometer];
}
}
// 必须要 移除观察者,
- (void)dealloc{
// 移除本控制器所有的通知;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}