使用CMMotionManager iPhone的抖动

时间:2013-09-10 22:30:16

标签: iphone ios cmmotionmanager

我正在使用CMMotionManager在我的视图上移动按钮,图像等,因为设备向前,向后,向右,向左倾斜。我得到姿态俯仰和滚动,然后使用这些来为表示屏幕上像素位置的字符串赋值。

以下是我获取信息的方式

motionManager = [[CMMotionManager alloc] init];
                   motionManager.deviceMotionUpdateInterval = 0.05f;
                   [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
                       NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
                       [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
                       [formatter setMaximumFractionDigits:2];
                       [formatter setRoundingMode: NSNumberFormatterRoundUp];
                       numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.attitude.roll /8]];
                       numberString1 = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.attitude.pitch /8]];
                   }];

这就是我分配它们的方式。

NSString *deltax = numberString;
    NSString *deltay = numberString1;
    NSLog(@"xString;%@",numberString);
    if ([deltax isEqualToString:@"-0.01"]) {
        xString = @"160";}
    else if ([deltax isEqualToString:@"-0.02"]) {
        xString = @"161";}
    else if ([deltax isEqualToString:@"-0.03"]) {
        xString = @"162";}
    else if ([deltax isEqualToString:@"-0.04"]) {
        xString = @"163";}

我已经制作了它们,因此x轴在屏幕上连续上下(来回)。即-.01与-.40,.01和.40相同。从-01和.01开始,它们的接近程度相同,值越大,从-40和4.4越小。我也对y轴做了同样的事情。 -.01和.01是284和-.20,.20是264.但是在这种情况下,数字从-01上升到.01。

这里有一些例子

//x axis

if ([deltax isEqualToString:@"-0.01"]) {
        xString = @"160";}
    else if ([deltax isEqualToString:@"-0.02"]) {
        xString = @"161";}
    else if ([deltax isEqualToString:@"-0.03"]) {
        xString = @"162";}
    else if ([deltax isEqualToString:@"-0.04"]) {
        xString = @"163";}
    else if ([deltax isEqualToString:@"-0.37"]) {
        xString = @"156";}
    else if ([deltax isEqualToString:@"-0.38"]) {
        xString = @"157";}
    else if ([deltax isEqualToString:@"-0.39"]) {
        xString = @"158";}
    else if ([deltax isEqualToString:@"-0.4"]) {
        xString = @"159";}
    else if ([deltax isEqualToString:@"0.01"]) {
        xString = @"159";}
    else if ([deltax isEqualToString:@"0.02"]) {
        xString = @"158";}
    else if ([deltax isEqualToString:@"0.03"]) {
        xString = @"157"; }
    else if ([deltax isEqualToString:@"0.04"]) {
        xString = @"156";}
    else if ([deltax isEqualToString:@"0.37"]) {
        xString = @"163";}
    else if ([deltax isEqualToString:@"0.38"]) {
        xString = @"162";}
    else if ([deltax isEqualToString:@"0.39"]) {
        xString = @"161";}
    else if ([deltax isEqualToString:@"0.4"]) {
        xString = @"160";}

//y axis




if ([deltay isEqualToString:@"-0.01"]) {
                yString = @"284";}
            else if ([deltay isEqualToString:@"-0.02"]) {
                yString = @"285";}
            else if ([deltay isEqualToString:@"-0.03"]) {
                yString = @"286";}
            else if ([deltay isEqualToString:@"-0.04"]) {
                yString = @"287";}
            else if ([deltay isEqualToString:@"-0.17"]) {
                yString = @"300"; }
            else if ([deltay isEqualToString:@"-0.18"]) {
                yString = @"301";}
            else if ([deltay isEqualToString:@"-0.19"]) {
                yString = @"302";}
            else if ([deltay isEqualToString:@"-0.2"]) {
                yString = @"303";}
            else if ([deltay isEqualToString:@"0.01"]) {
                yString = @"283";}
            else if ([deltay isEqualToString:@"0.02"]) {
                yString = @"282";}
            else if ([deltay isEqualToString:@"0.03"]) {
                yString = @"281";}
            else if ([deltay isEqualToString:@"0.04"]) {
                yString = @"280"; }
            else if ([deltay isEqualToString:@"0.17"]) {
                yString = @"267";}
            else if ([deltay isEqualToString:@"0.18"]) {
                yString = @"266";}
            else if ([deltay isEqualToString:@"0.19"]) {
                yString = @"265";}
            else if ([deltay isEqualToString:@"0.2"]) {
                yString = @"264";}

通过这种方式,从一个方向到下一个方向有连续的流动。

但是我遇到了一个小问题。当手机向前倾斜并从.2 faceUp方向转到.2 faceDown方向时。我得到一个生涩的动作。在faceUp .2按钮等突然向左移动,当它移动到faceDown .20时,它们向右移动。否则按钮等以.19和-19为中心并返回以正常操作(如预期的那样)。我试图调用DeviceOrientationFaceUp和FaceDown,但这似乎没有帮助。

是否有任何人有此行为的经验并知道修复或有任何建议。

我还添加了这个部分来上下左右移动按钮。无论手机的位置在哪里,都可以使用此方法上下左右按钮。

//used to assign numerical values
NSString *string2 = xString;
int n = [string2 intValue];
NSLog(@"n:%d",n);
NSString *string3 = xString1;
int p = [string3 intValue];
NSLog(@"p:%d",p);
NSString *string4 = yString;
int q = [string4 intValue];
NSLog(@"q:%d",q);
NSString *string5 = yString1;
int r = [string5 intValue];
NSLog(@"r:%d",r);

//used to move the buttons etc.
   imageView2.center = CGPointMake(p, q);
            imageView.center = CGPointMake(n -63, q);
            imageframe.center = CGPointMake(n -63, q);
            textView.center = CGPointMake(n -62, q - 184);
            label1.center = CGPointMake(n  + 97, q  - 195 );
            english.center = CGPointMake(n + 97, q - 159);
这是一种视差运动的东西。

2 个答案:

答案 0 :(得分:4)

一些反应:

  1. 我可能会建议使用gravity而不是attitude,因为它很好地代表了设备的物理方向。

  2. 我可能只是更新数值,而不是使用字符串来保存值。

  3. 而不是所有这些if语句,只需要一个表示视图应如何放置的公式。

  4. 如果你想移动一堆视图,你应该将它们放在UIView(通常称为“容器视图”中,不要与IB中的自定义容器控件混淆名称),然后只需移动容器视图。

  5. 因此,如果使用autolayout,我可能会有以下属性:

    @property (nonatomic, strong) CMMotionManager *motionManager;
    @property CGFloat top;
    @property CGFloat left;
    

    然后我可以像这样更新自动布局约束:

    self.motionManager = [[CMMotionManager alloc] init];
    self.motionManager.deviceMotionUpdateInterval = 0.05f;
    
    self.top = self.topConstraint.constant;
    self.left = self.leadingConstraint.constant;
    
    [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
        self.leadingConstraint.constant = self.left - motion.gravity.x / 8.0 * 100.0;
        self.topConstraint.constant     = self.top  - motion.gravity.y / 8.0 * 100.0;
    }];
    

    或者如果不使用autolayout:

    @property (nonatomic, strong) CMMotionManager *motionManager;
    @property CGPoint originalCenter;
    

    self.motionManager = [[CMMotionManager alloc] init];
    self.motionManager.deviceMotionUpdateInterval = 0.05f;
    
    self.originalCenter = self.containerView.center;
    
    [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
        self.containerView.center = CGPointMake(self.originalCenter.x - motion.gravity.x / 8.0 * 100.0, self.originalCenter.y - motion.gravity.y / 8.0 * 100.0);
    }];
    

    坦率地说,让运动管理器在主队列中运行会给我带来意志,所以我可能(a)为运动更新创建一个NSOperationQueue并且只更新一些类属性; (b)使用CADisplayLink(QuartzCore.framework的一部分)更新视图(如果需要)。因此:

    @property (nonatomic, strong) CMMotionManager *motionManager;
    @property (nonatomic, strong) NSOperationQueue *motionQueue;
    
    @property (nonatomic) CMAcceleration gravity;
    @property (nonatomic) CGPoint originalCenter;
    @property (nonatomic) BOOL updatePosition;
    
    @property (nonatomic, strong) NSMutableArray *angles;
    @property (nonatomic, strong) CADisplayLink *displayLink;
    

    - (void)viewDidDisappear:(BOOL)animated
    {
        [self stopDisplayLink];
        [self.motionManager stopDeviceMotionUpdates];
        self.motionManager = nil;
        self.motionQueue = nil;
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];    
    
        self.motionManager = [[CMMotionManager alloc] init];
        self.motionManager.deviceMotionUpdateInterval = 0.05f;
    
        self.motionQueue = [[NSOperationQueue alloc] init];
        self.motionQueue.name = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".motion"];
    
        self.originalCenter = self.containerView.center;
        self.updatePosition = NO;
    
        [self.motionManager startDeviceMotionUpdatesToQueue:self.motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
            @synchronized(self) {
                self.gravity = motion.gravity;
                self.updatePosition = YES;
            }
        }];
    
        [self startDisplayLink];
    }
    
    - (void)startDisplayLink
    {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)stopDisplayLink
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink
    {
        @synchronized(self) {
            if (!self.updatePosition)
                return;
    
            self.containerView.center = CGPointMake(self.originalCenter.x - self.gravity.x / 8.0 * 100.0, self.originalCenter.y - self.gravity.y / 8.0 * 100.0);
    
            self.updatePosition = NO;
        }
    }
    

答案 1 :(得分:0)

我做了以下事情: 在可调窗口内采用最大值和最小值(闪烁噪声)并取平均值的数学运算。 没有更多的振荡!

这是我的代码,以避免在may app中闪烁:

float xAxis,yAxis, zAxis;
xAxis = self.manager.accelerometerData.acceleration.x;
yAxis = self.manager.accelerometerData.acceleration.y;
zAxis = self.manager.accelerometerData.acceleration.z;

// returns if the phone is lying on the table:
if (zAxis < -0.8 || zAxis > 0.8) return;

CGFloat angle =  atan2f(xAxis, yAxis ); // The angle!!!

float noise = 0.011; // Flicker noise (the noise you want to filter)

// max and min are global variables
if (angle > max){
    max = angle;
    min = max - noise;
}
if (angle < min){
    min = angle;
    max = min + noise;
}

// Average: (no flickering):
angle = min + (max - min) / 2.0;