● 用于检测设备的运动(比如摇晃)
● 加速计的经典应用场景
● 摇一摇
● 计步器
● 在iOS4以前:使用UIAccelerometer,用法非常简单(到了iOS5就已经过 期)
● 从iOS4开始:CoreMotion.framework
● 虽然UIAccelerometer已经过期,但由于其用法极其简单,很多程序里面都 还有残留
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
● 设置代理 accelerometer.delegate = self;
● 设置采样间隔
accelerometer.updateInterval = 1.0/30.0; // 1秒钟采样30次
● 实现代理方法
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration
// acceleration中的x、y、z三个属性分别代表每个轴上的加速度
#import "ViewController.h" #import "UIView+Extension.h" @interface ViewController ()<UIAccelerometerDelegate> /** * 小球 */ @property (weak, nonatomic) IBOutlet UIImageView *imageBall; /** * 保存速度 */ @property (nonatomic, assign) CGPoint velocity; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 1.利用单利获取采集对象 UIAccelerometer *acc = [UIAccelerometer sharedAccelerometer]; // 2.设置代理 acc.delegate = self; // 3.设置采样时间 acc.updateInterval = 1 / 30; } #pragma mark -UIAccelerometerDelegate // 4.实现代理方法 /** * 只要采集到数据就会调用(调用频率非常高) * * @param accelerometer 触发事件的对象 * @param acceleration 获取到得数据 */ - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { NSLog(@"x = %f / y = %f / z = %f", acceleration.x, acceleration.y, acceleration.z); /* 速度 = 加速度 * 时间 V = at; == a * t1 + a * t2 + a * t3 ....; */ // 不能直接修改对象的结构体属性的成员 // self.velocity.x += acceleration.x; _velocity.x += acceleration.x; // -=的原因是因为获取到得Y轴的加速度和UIKit的坐标系的Y的值是相反的, 而我们将来想让小球往加速度的反方向运动, 所以 -=; _velocity.y -= acceleration.y; /* 移动的距离 = 速度 * 时间 S = vt; == v * t1 + v * t2 + v * t3 ....; */ self.imageBall.x += _velocity.x; self.imageBall.y += _velocity.y; // 边界检测 if (self.imageBall.x <= 0) { // 矫正小球当前的位置 self.imageBall.x = 0; // 超出了屏幕的左边 _velocity.x *= -0.5; } if (self.imageBall.y <= 0) { // 矫正小球当前的位置 self.imageBall.y = 0; // 超出屏幕的顶部 _velocity.y *= -0.5; } if (CGRectGetMaxY(self.imageBall.frame) >= self.view.height) { // 矫正小球当前的位置 self.imageBall.y = self.view.height - self.imageBall.height; // 查出屏幕的底部 _velocity.y *= -0.5; } if (CGRectGetMaxX(self.imageBall.frame) >= self.view.width) { // 矫正小球当前的位置 self.imageBall.x = self.view.width - self.imageBall.width; // 查出屏幕的右边 _velocity.x *= -0.5; } } @end
/* > 加速计 *PPT介绍加速计, 加速计属于传感器的范畴... *新建项目讲解加速计的传统使用 *查看Accelerometer头文件, 讲解Accelerometer头文件中的属性引出为什么过时了还那么多人用(简单, 查看头文件) *从代理方法参数引出定义属性保存小球加速度(CGPoint), 并在代理方法中设置小球的位置 *Vt = V0 + a * t (最终速度 = 初始速度 + 加速度 * 时间) Vt = V0 + g * t (加速度是恒定的9.8) Vt = V0 + g1 + g2 + g3 … (时间和加速度都是恒定的) Vt = V0 + a1 + a2 + a3 … (手机中时间是恒定的, 加速度不是恒定的) *从小球移除频幕看不到引出画图讲解边界检测(碰撞检测) *从小球会贴在边框出不来引出画图讲解如何解决(移动小球的位置到边界再加速度) *讲解为什么用两个if 不用else if, 小球在四个角的情况 */ #import "CZViewController.h" @interface CZViewController ()<UIAccelerometerDelegate> /** * 小球 */ @property (nonatomic, weak)UIImageView *ballIv; /** * 保存x y方向加速度 */ @property (nonatomic, assign)CGPoint ballVelocity; @end @implementation CZViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.添加小球 UIImage *image = [UIImage imageNamed:@"black"]; UIImageView *iv = [[UIImageView alloc] initWithImage:image]; [self.view addSubview:iv]; self.ballIv = iv; // 2.获得加速计单例 /** updateInterval 更新间隔(采样,采集数据样本) delegate 代理 */ UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; // 设置采样时间 accelerometer.updateInterval = 1 / 30.0; // 设置代理 accelerometer.delegate = self; } #pragma mark - UIAccelerometerDelegate // UIAcceleration 加速度 - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { // 1. 记录小球加速度 _ballVelocity.x += acceleration.x; // 加速计y轴方向和UIKit坐标相反 _ballVelocity.y -= acceleration.y; // 2.更新小球位置 [self updateLocation]; } - (void)updateLocation { // 设置小球的位置 CGPoint center = self.ballIv.center; CGSize size = self.view.bounds.size; // 解决小球出界问题,碰撞检测 // 水平方向:左边,右边 if (CGRectGetMinX(self.ballIv.frame) <= 0 || CGRectGetMaxX(self.ballIv.frame) >= size.width) { // 修改小球的速度方向 _ballVelocity.x *= -1; // 修复位置 < 0 if (CGRectGetMinX(self.ballIv.frame) <= 0) { center.x = self.ballIv.bounds.size.width / 2.0; } else { center.x = size.width - self.ballIv.bounds.size.width / 2.0; } } // 垂直方向 if (CGRectGetMinY(self.ballIv.frame) <= 0 || CGRectGetMaxY(self.ballIv.frame) >= size.height) { // 修改小球的速度方向 _ballVelocity.y *= -1; // 修复位置 < 0 if (CGRectGetMinY(self.ballIv.frame) <= 0) { center.y = self.ballIv.bounds.size.height / 2.0; } else { center.y = size.height - self.ballIv.bounds.size.height / 2.0; } } center.x += _ballVelocity.x; center.y += _ballVelocity.y; self.ballIv.center = center; } @end
● 实时采集所有数据(采集频率高)
● pull
● 在有需要的时候,再主动去采集数据
CMMotionManager *mgr = [[CMMotionManager alloc] init];
● 判断加速计是否可用(最好判断)
if (mgr.isAccelerometerAvailable) {
// 加速计可用 }
● 设置采样间隔
mgr.accelerometerUpdateInterval = 1.0/30.0; // 1秒钟采样30次
● 开始采样(采样到数据就会调用handler,handler会在queue中执行)
- (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue
withHandler:(CMAccelerometerHandler)handler;
● 创建运动管理者对象
● 判断加速计是否可用(最好判断)
if (mgr.isAccelerometerAvailable) { // 加速计可用 }
● 开始采样
- (void)startAccelerometerUpdates;
● 在需要的时候采集加速度数据
CMAcceleration acc = mgr.accelerometerData.acceleration; NSLog(@"%f, %f, %f", acc.x, acc.y, acc.z);
实例1:
#import "ViewController.h" #import <CoreMotion/CoreMotion.h> @interface ViewController () @property (nonatomic, strong) CMMotionManager *mgr; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.创建coreMotion管理者 self.mgr = [[CMMotionManager alloc] init]; if (self.mgr.isAccelerometerAvailable) { // 3.开始采样 [self.mgr startAccelerometerUpdates]; // pull }else { NSLog(@"加速计不可用"); } } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CMAcceleration acceleration = self.mgr.accelerometerData.acceleration; NSLog(@"x = %f y = %f z = %f", acceleration.x, acceleration.y , acceleration.z); } - (void)push { // 1.创建coreMotion管理者 // CMMotionManager *mgr = [[CMMotionManager alloc] init]; self.mgr = [[CMMotionManager alloc] init]; // 2.判断加速计是否可用 if (self.mgr.isAccelerometerAvailable) { /* isAccelerometerActive 是否正在采集 accelerometerData 采集到得数据 startAccelerometerUpdates pull startAccelerometerUpdatesToQueue push stopAccelerometerUpdates 停止采集 accelerometerUpdateInterval 采样时间 */ // 3.设置采样时间 self.mgr.accelerometerUpdateInterval = 1 / 30.0; // 4.开始采样 [self.mgr startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { // 这个block是采集到数据时就会调用 if (error) return ; CMAcceleration acceleration = accelerometerData.acceleration; NSLog(@"x = %f y = %f z = %f", acceleration.x, acceleration.y , acceleration.z); }]; }else { NSLog(@"加速计不可用"); } } @end
实例2:
#import "HMViewController.h" #import <CoreMotion/CoreMotion.h> @interface HMViewController () @property (nonatomic, strong) CMMotionManager *mgr; @end @implementation HMViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.创建motion管理者 self.mgr = [[CMMotionManager alloc] init]; // 2.判断加速计是否可用 if (self.mgr.isAccelerometerAvailable) { [self pull]; } else { NSLog(@"---加速计不可用-----"); } } /** * ******* pull ******* */ - (void)pull { [self.mgr startAccelerometerUpdates]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CMAcceleration acceleration = self.mgr.accelerometerData.acceleration; NSLog(@"%f %f %f", acceleration.x, acceleration.y, acceleration.z); } /** * ******* push ******* */ - (void)push { // 3.设置采样间隔 self.mgr.accelerometerUpdateInterval = 1 / 30.0; // 4.开始采样(采集加速度数据) [self.mgr startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { // 如果在block中执行较好时的操作,queue最好不是主队列 // 如果在block中要刷新UI界面,queue最好是主队列 NSLog(@"%f %f %f", accelerometerData.acceleration.x, accelerometerData.acceleration.y, accelerometerData.acceleration.z); }]; } @end
实例3:
#import "CZMotionManager.h" /** 1. 静态的全局实例 2. allocWithZone 3. 定义一个shared方法,供全局使用 */ // 保存当前类的实例 static CZMotionManager *_instance; @implementation CZMotionManager + (instancetype)shareMotionManager { if (_instance == nil) { _instance = [[[self class] alloc] init]; } return _instance; } #pragma mark - 以下方法是为了保证对象在任何情况下都唯一 // 调用alloc的时候会调用这个方法 + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; // 保证我们的block只能执行一次 dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } //会在调用copy的时候调用这个方法 - (id)copyWithZone:(NSZone *)zone { return self; } //在调用mutableCopy的时候调用这个方法 - (id)mutableCopyWithZone:(NSZone *)zone { return self; } @end
/* >PPT讲解CoreMontion *新建工程导入CoreMontion框架, 拷贝常规使用添加小球代码 *导入CoreMontion框架主头文件, 查看头文件讲解相关属性含义 *实例化运动管理器,—>判断加速计是否可用—>设置采样数据的时间间隔—>开始采样数据 —>更改小球位置 *从运行没有反应引出CoreMontionManage是局部变量 *新建队列替换主队列, 从运行程序没有任何反应引出修改更新方法在主队列中修改UI *从系统提供的CoreMontionManage不是单例,引出定义子类实现单例(讲解如何实现单例) *画图讲解从采样时间和刷帧时间不一样小球现实会不清晰引出采样和刷帧单独进行 *从加速计常用于游戏引出实现点击频幕后暂停游戏, 注意不需要每次都销毁CADisplayLink */ #import "CZViewController.h" #import <CoreMotion/CoreMotion.h> #import "CZMotionManager.h" @interface CZViewController () /** * 小球 */ @property (nonatomic, weak)UIImageView *ballIv; /** * 保存x y方向加速度 */ @property (nonatomic, assign)CGPoint ballVelocity; /** * 运动管理器 */ @property (nonatomic, strong)CMMotionManager *motionM; @property (nonatomic, strong)NSOperationQueue *queue; @property (nonatomic, strong)CADisplayLink *link; @end @implementation CZViewController /** 运动管理器的属性 1> accelerometerUpdateInterval 加速计更新间隔 2> isAccelerometerAvailable 是否可用 3> accelerometerActive 是否正在采集数据 4> accelerometerData 加速计数据(通过Pull方式获得) 5> startAccelerometerUpdatesToQueue 开始加速计更新(通过Push方式实时获得采样数据) 6> stopAccelerometerUpdates 停止数据采样 */ - (void)viewDidLoad { [super viewDidLoad]; // 1.添加小球 UIImage *image = [UIImage imageNamed:@"black"]; UIImageView *iv = [[UIImageView alloc] initWithImage:image]; [self.view addSubview:iv]; self.ballIv = iv; self.queue = [[NSOperationQueue alloc] init]; // 2.实例化运动管理器 // CMMotionManager *montionM = [[CMMotionManager alloc] init]; // self.motionM = [[CMMotionManager alloc] init]; self.motionM = [CZMotionManager shareMotionManager]; // 3.判断加速计是否可用 if (self.motionM.isAccelerometerAvailable) { // 3.1设置采样时间间隔 self.motionM.accelerometerUpdateInterval = 1 / 30.0; /* // 3.2 开始采样数据 // [self.motionM startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData * [self.motionM startAccelerometerUpdatesToQueue:self.queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { // NSLog(@"%@", [NSThread currentThread]); // 处理采样数据,修改小球速度 _ballVelocity.x += accelerometerData.acceleration.x; _ballVelocity.y -= accelerometerData.acceleration.y; // 更改小球位置 // [self updateLocation]; }]; */ // 3.2 开始采样数据 [self startAccelerometerUpdates]; }else { NSLog(@"摔坏了"); } // 4.实例化CADisplayLink /* 在真机实际运行中,小球的显示并是不非常的清晰,会有一些发虚。 */ CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateLocation)]; [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; self.link = link; } #pragma mark - 私有方法 #pragma mark 开始采样数据 - (void)startAccelerometerUpdates { // 3.2 开始采样数据 [self.motionM startAccelerometerUpdatesToQueue:self.queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { // 处理采样数据,修改小球速度 _ballVelocity.x += accelerometerData.acceleration.x; _ballVelocity.y -= accelerometerData.acceleration.y; }]; } - (void)updateLocation { // 设置小球的位置 CGPoint center = self.ballIv.center; CGSize size = self.view.bounds.size; // 解决小球出界问题,碰撞检测 // 水平方向:左边,右边 if (CGRectGetMinX(self.ballIv.frame) <= 0 || CGRectGetMaxX(self.ballIv.frame) >= size.width) { // 修改小球的速度方向 _ballVelocity.x *= -1; // 修复位置 < 0 if (CGRectGetMinX(self.ballIv.frame) <= 0) { center.x = self.ballIv.bounds.size.width / 2.0; } else { center.x = size.width - self.ballIv.bounds.size.width / 2.0; } } // 垂直方向 if (CGRectGetMinY(self.ballIv.frame) <= 0 || CGRectGetMaxY(self.ballIv.frame) >= size.height) { // 修改小球的速度方向 _ballVelocity.y *= -1; // 修复位置 < 0 if (CGRectGetMinY(self.ballIv.frame) <= 0) { center.y = self.ballIv.bounds.size.height / 2.0; } else { center.y = size.height - self.ballIv.bounds.size.height / 2.0; } } center.x += _ballVelocity.x; center.y += _ballVelocity.y; // NSLog(@"%@", [NSThread currentThread]); // 在主线程队列上更新小球位置 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.ballIv.center = center; }]; } // 游戏“暂停”——用户点击屏幕,暂停小球运动 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (self.motionM.isAccelerometerActive) { // 停止 [self.motionM stopAccelerometerUpdates]; /* invalidate停止时钟 1> 将时钟从主运行循环中撤销 2> 将时钟销毁 [_gameTimer invalidate]; */ // 直接从主运行循环中将游戏时钟删除,而不会销毁时钟,等到需要时再次添加即可。 [self.link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; }else { // 开始采样数据 [self startAccelerometerUpdates]; // 开启时钟 [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } } @end