CoreMotion框架(二)—— 利用加速度計(jì)獲取設(shè)備的方向

版本記錄

版本號 時(shí)間
V1.0 2017.08.05

前言

我們的app很多都需要獲取使用者的動(dòng)作、方向以及其他和方位或者位置有關(guān)的參數(shù),在ios中對應(yīng)的框架就是CoreMotion,而在硬件對應(yīng)的就是集成的加速計(jì)和陀螺儀。這幾篇我們就從基礎(chǔ)原理理論出發(fā),講一下相關(guān)的知識。關(guān)于這個(gè)框架的了解感興趣的可以看這幾篇。
1. CoreMotion框架(一)—— 基礎(chǔ)理論

功能要求

前面我寫過兩篇文章關(guān)于橫豎屏適配的問題,就是根據(jù)設(shè)備方向等進(jìn)行適配的。這里我要說另外一種方法,就是利用CoreMotion框架和加速器獲取設(shè)備的方向進(jìn)行適配的。


功能實(shí)現(xiàn)

使用加速器,ios5是一個(gè)分界線。

  • iOS4以前:使用UIAccelerometer,用法非常簡單(到了iOS5就已經(jīng)過期),雖然過期但是現(xiàn)在仍然可以用。
  • iOS4開始:CoreMotion.framework。

1. iOS5以前

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

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

@interface JJAccelerometerVC () <UIAccelerometerDelegate>

@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次,所以輸出的很快。給大家隨便粘出來幾個(gè)輸出值。

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以后

加速計(jì)和陀螺儀的值都是通過Core Motion框架訪問的,應(yīng)用程序創(chuàng)建一個(gè)CMMotionManager實(shí)例,然后通過以下某種模式使用它:

  • 可以在動(dòng)作發(fā)生時(shí)執(zhí)行一些代碼。
  • 可以時(shí)刻監(jiān)視一個(gè)持續(xù)更新的結(jié)構(gòu),是你隨時(shí)能夠訪問到最新的值。

對于數(shù)據(jù)的獲取和處理,也有兩種方式,一種是pull,另外一種是push

  • push :實(shí)時(shí)采集所有數(shù)據(jù)(時(shí)刻監(jiān)視,采集頻率高),這個(gè)是框架按照一定的采用頻率自動(dòng)返回的,具體多上時(shí)間發(fā)update一次,就看你怎么設(shè)置了。
  • pull :在有需要的時(shí)候,再主動(dòng)去采集數(shù)據(jù)(基于事件的動(dòng)作)。這個(gè)就看開發(fā)者什么時(shí)候調(diào)用什么時(shí)候就會(huì)返回一次,框架不會(huì)主動(dòng)給返回。

ios5以后,我們看下面代碼。

push

//push
1. JJGravityVC.m
#import "JJGravityVC.h"
#import <CoreMotion/CoreMotion.h>

@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 {
                //在這里進(jìn)行屏幕方向的判斷
//                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);一直不停止的回調(diào),因此可以在獲取想要的結(jié)果以后,調(diào)用下面這個(gè)方法。
- (void)stopAcceleration
{
    if ([self.motionManager isAccelerometerAvailable]) {
        [self.motionManager stopAccelerometerUpdates];
    }
}

@end

這里我采用的是push的方式,系統(tǒng)會(huì)自動(dòng)的回調(diào)數(shù)據(jù),我設(shè)置的采樣就是1s采樣15次,大約67msupdate一次數(shù)據(jù),我們看下面輸出。

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

確實(shí)是這樣的。

pull

接下來我們看一下pull形式的代碼怎么去寫,pull其實(shí)就是基于事件進(jìn)行驅(qū)動(dòng)的,有事件需要的才會(huì)觸發(fā)。

//pull
1. JJPullAcceleratorVC.m
#import "JJPullAcceleratorVC.h"
#import <CoreMotion/CoreMotion.h>

@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<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //pull,基于事件進(jìn)行驅(qū)動(dòng),有事件才會(huì)去調(diào)用
    if ([self.motionManager isAccelerometerAvailable]) {
        //開始采用,并獲得采樣數(shù)據(jù)
        [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

下面看一下輸出結(jié)果

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

我這里是點(diǎn)擊一下屏幕就采樣一次,不會(huì)像push那樣,以固定的采樣率進(jìn)行采樣。

后記

未完,待續(xù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容