CoreMotion框架(二)—— 利用加速度计获取设备的方向

版本记录

版本号 时间
V1.0 2017.08.05

前言

我们的app很多都需要获取使用者的动作、方向以及其他和方位或者位置有关的参数,在ios中对应的框架就是CoreMotion,而在硬件对应的就是集成的加速计和陀螺仪。这几篇我们就从基础原理理论出发,讲一下相关的知识。关于这个框架的了解感兴趣的可以看这几篇。
1. CoreMotion框架(一)—— 基础理论

功能要求

前面我写过两篇文章关于横竖屏适配的问题,就是根据设备方向等进行适配的。这里我要说另外一种方法,就是利用CoreMotion框架和加速器获取设备的方向进行适配的。


功能实现

使用加速器,ios5是一个分界线。

  • iOS4以前:使用UIAccelerometer,用法非常简单(到了iOS5就已经过期),虽然过期但是现在仍然可以用。
  • iOS4开始:CoreMotion.framework

1. iOS5以前

iOS5以前使用的是类UIAccelerometer,它属于UIKit框架里面的。

1. JJAccelerometerVC.m
#import "JJAccelerometerVC.h"

@interface JJAccelerometerVC () 

@end

@implementation JJAccelerometerVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIAccelerometer *acclerometer = [UIAccelerometer sharedAccelerometer];
    acclerometer.updateInterval = 1.0/15;
    acclerometer.delegate = self;
}

#pragma mark - UIAccelerometerDelegate

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    UIAccelerationValue x = acceleration.x;
    UIAccelerationValue y = acceleration.y;
    UIAccelerationValue z = acceleration.z;
    
    if (fabs(x) <= fabs(y)) {
        if (y >= 0) {
            NSLog(@"UIDeviceOrientationPortraitUpsideDown");
        }
        else {
            NSLog(@"UIDeviceOrientationPortrait");
        }
    }
    else {
        if (x >= 0) {
            NSLog(@"UIDeviceOrientationLandscapeRight");
        }
        else {
            NSLog(@"UIDeviceOrientationLandscapeLeft");
        }
    }
}

@end

这里,采样频率为1s15次,所以输出的很快。给大家随便粘出来几个输出值。

2017-08-05 18:00:44.555400+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait
2017-08-05 18:00:44.622714+0800 JJOC[7804:3020881] UIDeviceOrientationLandscapeRight
2017-08-05 18:00:44.689951+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait
2017-08-05 18:00:44.757171+0800 JJOC[7804:3020881] UIDeviceOrientationLandscapeRight
2017-08-05 18:00:44.824398+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait
2017-08-05 18:00:44.891813+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait
2017-08-05 18:00:44.958975+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait
2017-08-05 18:00:45.026187+0800 JJOC[7804:3020881] UIDeviceOrientationPortrait

2. iOS5以后

加速计和陀螺仪的值都是通过Core Motion框架访问的,应用程序创建一个CMMotionManager实例,然后通过以下某种模式使用它:

  • 可以在动作发生时执行一些代码。
  • 可以时刻监视一个持续更新的结构,是你随时能够访问到最新的值。

对于数据的获取和处理,也有两种方式,一种是pull,另外一种是push

  • push :实时采集所有数据(时刻监视,采集频率高),这个是框架按照一定的采用频率自动返回的,具体多上时间发update一次,就看你怎么设置了。
  • pull :在有需要的时候,再主动去采集数据(基于事件的动作)。这个就看开发者什么时候调用什么时候就会返回一次,框架不会主动给返回。

ios5以后,我们看下面代码。

push

//push
1. JJGravityVC.m
#import "JJGravityVC.h"
#import 

@interface JJGravityVC ()

@property (nonatomic, assign) NSTimeInterval updateInterval;
@property (nonatomic, strong) CMMotionManager *motionManager;

@end

@implementation JJGravityVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];
    
    self.updateInterval = 1.0/15.0;
    self.motionManager = [[CMMotionManager alloc] init];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    if ([self.motionManager isAccelerometerAvailable]) {
        self.motionManager.accelerometerUpdateInterval = self.updateInterval;
        [self.motionManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
            if (error) {
                NSLog(@"%@",error.description);
            }
            else {
                //在这里进行屏幕方向的判断
//                NSLog(@"x = %lf, y = %lf, z = %lf",accelerometerData.acceleration.x, accelerometerData.acceleration.y, accelerometerData.acceleration.z);
                
                CGFloat x = accelerometerData.acceleration.x;
                CGFloat y = accelerometerData.acceleration.y;
                CGFloat z = accelerometerData.acceleration.z;
                
                if (fabs(x) <= fabs(y)) {
                    if (y >= 0) {
                        NSLog(@"UIDeviceOrientationPortraitUpsideDown");
                    }
                    else {
                        NSLog(@"UIDeviceOrientationPortrait");
                    }
                }
                else {
                    if (x >= 0) {
                        NSLog(@"UIDeviceOrientationLandscapeRight");
                    }
                    else {
                        NSLog(@"UIDeviceOrientationLandscapeLeft");
                    }
                }
            }
        }];
    }
    else {
        NSLog(@"The device does not support accelerometer!");
    }
}

- (void)dealloc
{
    self.motionManager = nil;
}

#pragma mark - Object Private Function

//上面的NSLog(@"x = %lf, y = %lf, z = %lf",accelerometerData.acceleration.x, accelerometerData.acceleration.y, accelerometerData.acceleration.z);一直不停止的回调,因此可以在获取想要的结果以后,调用下面这个方法。
- (void)stopAcceleration
{
    if ([self.motionManager isAccelerometerAvailable]) {
        [self.motionManager stopAccelerometerUpdates];
    }
}

@end

这里我采用的是push的方式,系统会自动的回调数据,我设置的采样就是1s采样15次,大约67msupdate一次数据,我们看下面输出。

2017-08-05 15:50:42.527385+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.594454+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.661608+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.728912+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.796129+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.863421+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.930696+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:42.998406+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.065164+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.132831+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.199666+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.266782+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.334437+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.401540+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.468730+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.536152+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.604181+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.670330+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.737794+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.804924+0800 JJOC[7766:2999274] UIDeviceOrientationLandscapeLeft
2017-08-05 15:50:43.872237+0800 JJOC[7766:2999321] UIDeviceOrientationLandscapeLeft

确实是这样的。

pull

接下来我们看一下pull形式的代码怎么去写,pull其实就是基于事件进行驱动的,有事件需要的才会触发。

//pull
1. JJPullAcceleratorVC.m
#import "JJPullAcceleratorVC.h"
#import 

@interface JJPullAcceleratorVC ()

@property (nonatomic, strong) CMMotionManager *motionManager;
@property (nonatomic, assign) CMAcceleration acc;

@end

@implementation JJPullAcceleratorVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];

    self.motionManager = [[CMMotionManager alloc] init];
}

- (void)dealloc
{
    self.motionManager = nil;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //pull,基于事件进行驱动,有事件才会去调用
    if ([self.motionManager isAccelerometerAvailable]) {
        //开始采用,并获得采样数据
        [self.motionManager startAccelerometerUpdates];
        self.acc = self.motionManager.accelerometerData.acceleration;
        [self startAccelerometer];
    }
}

#pragma mark - Object Private Function

- (void)stopAcceleration
{
    if ([self.motionManager isAccelerometerAvailable]) {
        [self.motionManager stopAccelerometerUpdates];
    }
}

- (void)startAccelerometer
{
    CGFloat x = self.acc.x;
    CGFloat y = self.acc.y;
    CGFloat z = self.acc.z;
    
    if (fabs(x) <= fabs(y)) {
        if (y >= 0) {
            NSLog(@"UIDeviceOrientationPortraitUpsideDown");
        }
        else {
            NSLog(@"UIDeviceOrientationPortrait");
        }
    }
    else {
        if (x >= 0) {
            NSLog(@"UIDeviceOrientationLandscapeRight");
        }
        else {
            NSLog(@"UIDeviceOrientationLandscapeLeft");
        }
    }
}

@end

下面看一下输出结果

2017-08-05 16:53:12.998816+0800 JJOC[7796:3011684] UIDeviceOrientationLandscapeRight
2017-08-05 16:53:13.201374+0800 JJOC[7796:3011684] UIDeviceOrientationLandscapeRight
2017-08-05 16:53:13.385063+0800 JJOC[7796:3011684] UIDeviceOrientationLandscapeRight
2017-08-05 16:53:13.568807+0800 JJOC[7796:3011684] UIDeviceOrientationLandscapeRight
2017-08-05 16:53:14.849985+0800 JJOC[7796:3011684] UIDeviceOrientationPortraitUpsideDown
2017-08-05 16:53:15.016851+0800 JJOC[7796:3011684] UIDeviceOrientationPortraitUpsideDown
2017-08-05 16:53:15.217770+0800 JJOC[7796:3011684] UIDeviceOrientationPortraitUpsideDown
2017-08-05 16:53:15.367570+0800 JJOC[7796:3011684] UIDeviceOrientationPortraitUpsideDown
2017-08-05 16:53:15.516851+0800 JJOC[7796:3011684] UIDeviceOrientationPortraitUpsideDown

我这里是点击一下屏幕就采样一次,不会像push那样,以固定的采样率进行采样。

后记

未完,待续~~~

CoreMotion框架(二)—— 利用加速度计获取设备的方向_第1张图片

你可能感兴趣的:(CoreMotion框架(二)—— 利用加速度计获取设备的方向)