來(lái)!我們來(lái)玩一下陀螺儀和加速度計(jì)

先來(lái)看一下效果
111.gif

這是通過(guò)調(diào)用iOS的陀螺儀和加速度計(jì)。通過(guò)傾斜手機(jī)實(shí)現(xiàn)的效果

Core Motion

加速度計(jì)和陀螺儀是通過(guò)Core Motion框架,來(lái)管理訪問(wèn)的。此框架提供了CMMotionManager類,它提供的數(shù)據(jù)都是用來(lái)描述設(shè)備的移動(dòng)的。還提供了CMAccelerometerData和CMGroData,他們可以訪問(wèn)原生的加速度計(jì)和陀螺儀信息。另外還有CMDeviceMotion,這個(gè)類里面包含有加速度計(jì)和陀螺儀的測(cè)量數(shù)據(jù),以及方位信息。即設(shè)備是平放,朝上,朝下,還是朝左等信息。話不多說(shuō)還是直接上代碼。
  先來(lái)個(gè)坐標(biāo)系作為參考


屏幕快照 2016-07-09 下午5.24.42.png
看一下各項(xiàng)數(shù)據(jù)
#define kUpdateInterval (1.f / 60.f)//定義一個(gè)更新時(shí)間
@property (nonatomic,strong)CMMotionManager *motionManager;
@property (nonatomic,strong)NSOperationQueue *quene;
//聲明一個(gè)線程

self.motionManager = [[CMMotionManager alloc]init];
    NSOperationQueue *quene = [[NSOperationQueue alloc]init];
初始化一個(gè)線程,動(dòng)作管理器需要一個(gè)單獨(dú)的線程來(lái)運(yùn)行,以便在每次發(fā)生事件時(shí)放入要執(zhí)行的工作。蘋果文檔之處不要放在默認(rèn)隊(duì)列,可能會(huì)被事件填滿,無(wú)法處理別的事件

    if (self.motionManager.deviceMotionAvailable) {
        self.motionManager.deviceMotionUpdateInterval = 0.1;
        //設(shè)置更新數(shù)據(jù)的時(shí)間間隔
        [self.motionManager startDeviceMotionUpdatesToQueue: quene withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
            CMRotationRate rotationRate = motion.rotationRate;
           //旋轉(zhuǎn)速率對(duì)應(yīng)x,y,z分別是設(shè)備繞x,y,z 軸(上面有圖參考)
           的旋轉(zhuǎn)速率,順時(shí)針為正,逆時(shí)針為負(fù)
            CMAcceleration gravity = motion.gravity;
            //自然加速度,可是識(shí)別手機(jī)的放置方向,設(shè)備平放在桌上,z=-1;
               屏幕朝左側(cè)立,x = -1;充電口朝下垂直放置,y=-1
            CMAcceleration userAcc = motion.userAcceleration;
            //用戶運(yùn)動(dòng)手機(jī)造成的加速度,分別向xyz方向運(yùn)動(dòng)時(shí),正向時(shí)即為正值
            CMAttitude *attitude = motion.attitude;
            //設(shè)備的旋轉(zhuǎn)程度,也可以理解為設(shè)備繞xyz軸旋轉(zhuǎn)的程度,單位是
            弧度,即旋轉(zhuǎn)一周的值為2π,手機(jī)繞舉個(gè)例子:把手機(jī)平放在桌面上,
            旋轉(zhuǎn)手機(jī)Yaw(偏移)開(kāi)始變化,旋轉(zhuǎn)180°時(shí)yaw = 3.14;
            繞Y軸旋轉(zhuǎn)時(shí)Roll變化;繞X軸旋轉(zhuǎn)時(shí)Pitch變化(方向不在多做討論,參考系不一樣,沒(méi)法統(tǒng)一)
            NSString *gyroscopeText = [NSString   stringWithFormat:@"Rotation:\\n x = %.2f\\n y = %.2f\\n 
  z = %.2f",rotationRate.x,rotationRate.y,rotationRate.z];
            NSString *acceleratiorText = [NSString stringWithFormat:@"Accelertion: \\n Gravity x = %.2f\\tUser x = %.2f 
\\n Gravity y = %.2f\\tUser y = %.2f \\n Gravity z = %.2f\\tUser z = %.2f \\n",gravity.x,userAcc.x,gravity.y,userAcc.y,gravity.z,userAcc.z];
            
            NSString *attitudeText = [NSString stringWithFormat:@"Attitute:\\n Roll = %.2f\\nPitch = %.2f
\\nYaw = %.2f\\n",attitude.roll,attitude.pitch,attitude.yaw];
//            NSLog(@"%@",gyroscopeText);
            dispatch_async(dispatch_get_main_queue(), ^{
                _gyroscopeLabel.text = gyroscopeText;
                _accelerometerLabel.text = acceleratiorText;
                _attitudeLabel.text = attitudeText;
            });
        }];
    }

然后我們來(lái)做一個(gè)Demo

controller中代碼實(shí)現(xiàn)

#define kUpdateInterval (1.f / 60.f)//更新頻率高一點(diǎn)
@interface BallViewController ()
@property (nonatomic,strong)CMMotionManager *motionManager;
@property (nonatomic,strong)NSOperationQueue *quene;
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    BallView *ballView = [[BallView alloc]initWithFrame:self.view.bounds];
//自己寫的類繼承自UIView
    [self.view addSubview:ballView];
    self.motionManager = [[CMMotionManager alloc]init];
    self.quene = [[NSOperationQueue alloc]init];
    self.motionManager.deviceMotionUpdateInterval = kUpdateInterval;
    [self.motionManager startDeviceMotionUpdatesToQueue:_quene withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
//此處也可以傳入motion.userAcceleration。(用戶移動(dòng)手機(jī)產(chǎn)生的加速度)這樣就可以實(shí)現(xiàn)搖動(dòng)來(lái)控制(ballView里面的放大倍數(shù)記得要改大一點(diǎn))
        [ballView setAcceleration:motion.gravity];
        dispatch_async(dispatch_get_main_queue(), ^{
        [ballView update];
       //主線程更新UI
        });
    }];
}

BallView的實(shí)現(xiàn)
.h文件

#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
@interface BallView : UIView
@property (nonatomic,assign)CMAcceleration acceleration;
- (void)update;
@end

.m文件

@interface BallView ()
@property (nonatomic,assign)CGFloat width;
@property (nonatomic,assign)CGFloat height;
@property (nonatomic,assign)CGFloat imageWidth;
@property (nonatomic,assign)CGFloat imageHeight;
@property (nonatomic,strong)UIImage *image;
@property (nonatomic,strong)UIImageView *imageView;
@property (nonatomic,assign)CGPoint currentPoint;//當(dāng)前ImageView位置
@property (nonatomic,assign)CGFloat ballXVelocity;//X方向速度
@property (nonatomic,assign)CGFloat ballYVelocity;//Y方向速度
@end

@implementation BallView

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self commoninit];
        self.backgroundColor = [UIColor whiteColor];
    }
    return self;
}
- (void)commoninit{
    self.image = [UIImage imageNamed:@"btn_nav04@3x"];
    self.imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.imageWidth, self.imageHeight)];
    self.imageView.image = _image;
    [self addSubview:self.imageView];
    self.currentPoint = CGPointMake(self.bounds.size.width/2.f+self.image.size.width/2.f, self.bounds.size.height/2.f+self.image.size.height/2.f);
    self.imageView.center = _currentPoint;
}

- (void)setCurrentPoint:(CGPoint)newPoint{
    _currentPoint = newPoint;
        //邊緣判斷
    if (self.currentPoint.x <= self.imageWidth/2.f) {
        _currentPoint.x = self.imageWidth/2.f;
        self.ballXVelocity = -_ballXVelocity/2.f;
        //反彈效果,接觸邊緣后加速度反向減半
    }
    if (self.currentPoint.y <= self.imageHeight/2.f) {
        _currentPoint.y = self.imageHeight/2.f;
        self.ballYVelocity = -_ballYVelocity/2.f;
        //反彈效果,接觸邊緣后加速度反向減半
    }
    if (self.currentPoint.x >= self.width -self.imageWidth/2.f ) {
        _currentPoint.x = self.width -self.imageWidth/2.f;
        self.ballXVelocity = -_ballXVelocity/2.f;
        //反彈效果,接觸邊緣后加速度反向減半
    }
    if (self.currentPoint.y >= self.height -self.imageHeight/2.f ) {
        _currentPoint.y = self.height -self.imageHeight/2.f;
        self.ballYVelocity = -_ballYVelocity/2.f;
       //反彈效果,接觸邊緣后加速度反向減半
    }
    [self updateCenter];
}
- (void)updateCenter{
    self.imageView.center = _currentPoint;
}

- (void)update{
    static NSDate *lastUpdateTime = nil;
    if (lastUpdateTime != nil) {
        NSTimeInterval secondsSinceLastDraw = [[NSDate date] timeIntervalSinceDate:lastUpdateTime];//兩次更新的時(shí)間差
        self.ballXVelocity = self.ballXVelocity + (self.acceleration.x * secondsSinceLastDraw);
        //X方向上原速度加上加速度乘以時(shí)間,計(jì)算當(dāng)前速度
        self.ballYVelocity = self.ballYVelocity - (self.acceleration.y * secondsSinceLastDraw);
        //Y方向上原速度加上加速度乘以時(shí)間,計(jì)算當(dāng)前速度
        CGFloat xAccel = secondsSinceLastDraw * self.ballXVelocity * 1000;
        //計(jì)算位置變化量。由于這個(gè)值很小,所以我們要放大一些才更加真實(shí)此處
        //若想使用搖動(dòng),要把放大倍數(shù)調(diào)大,10000效果不錯(cuò)
        CGFloat yAccel = secondsSinceLastDraw * self.ballYVelocity * 1000;
        self.currentPoint = CGPointMake(self.currentPoint.x + xAccel, self.currentPoint.y + yAccel);
    }
    lastUpdateTime = [NSDate date];
}
#pragma mark - widthAndHeight
- (CGFloat)width{
    return self.bounds.size.width;
}
- (CGFloat)height{
    return self.bounds.size.height;
}
- (CGFloat)imageWidth{
    return self.image.size.width;
}
- (CGFloat)imageHeight{
    return self.image.size.height;
}

這樣你就可以實(shí)現(xiàn),傾斜手機(jī)來(lái)移動(dòng)圖片,就像真是的重力產(chǎn)生的效果一樣。同樣也可以實(shí)現(xiàn),搖動(dòng)的效果。(就像安卓的360原來(lái)的版本有個(gè)搖動(dòng)的球清理內(nèi)存那樣的效果)。
  如需轉(zhuǎn)載請(qǐng)注明出處,請(qǐng)尊重作者的勞動(dòng)成果。

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

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

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