【问题标题】:jerky motion using CMMotionManager iPhone使用 CMMotionManager iPhone 的生涩动作
【发布时间】:2013-09-10 22:30:15
【问题描述】:

当设备向前、向后、向右、向左倾斜时,我正在使用 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 上升到 0.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";}

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

但是我遇到了一个小问题。当手机向前倾斜并从 0.2 面朝上方向转变为 0.2 面朝下方向时。我有一个生涩的动作。在 faceUp .2 按钮等中,突然向左移动,当它移动到 faceDown .20 时,它们向右移动。否则,按钮等以 0.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);

这是一种视差运动的东西。

【问题讨论】:

  • 我发现在寻找基本方向的东西时使用gravity 比使用attitude 更容易。我也只会使用数值(在自动布局中调整约束constant,如果没有调整frame 值),这消除了所有这些if 语句。
  • 不确定您的意思。但是我尝试过使用加速度值,但是当我使用它们时,当手机向前倾斜时按钮向下移动但直到手机返回通过平面朝上位置时才向上移动。此外,我还添加了一些用于移动原始帖子中的按钮的代码。
  • 你能给我一些你建议的例子吗

标签: iphone ios cmmotionmanager


【解决方案1】:

几个反应:

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

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

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

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

所以,如果使用自动布局,我可能有以下属性:

@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;
}];

或者如果不使用自动布局:

@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;
    }
}

【讨论】:

  • 好的,这很好用,但是当我转到 faceDown 方向时,运动是相反的,我希望它们继续它们在 faceUp 中的方式。特别是在 x 轴上。你知道我将如何应用设备方向限定符吗?那我就加而不是减。
  • @user1114881 你可以用你想要的任何东西替换那个函数(添加重力而不是减去)。如果您支持不同的方向,只需查看[[UIDevice currentDevice] orientation],如果横向可能交换gravity.xgravity.y 的引用。同样,您可能需要根据UIDeviceOrientationPortraitUpsideDownUIDeviceOrientationPortrait 调整符号(同样UIDeviceOrientationLandscapeLeftUIDeviceOrientationLandscapeRight)。我相信通过一些实验你会得到正确的值。
  • 感谢我猜是 FaceUp 和 FaceDown 在实现方向时遇到了一点麻烦。我不使用风景。无论如何谢谢
【解决方案2】:

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

这是我的代码,以避免在可能的应用程序中闪烁:

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-31
    • 1970-01-01
    • 1970-01-01
    • 2012-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多