Home > Articles > Mobile Application Development & Programming

  • Print
  • + Share This
This chapter is from the book

Recipe: Using Acceleration to Move Onscreen Objects

With a bit of programming, the iPhone’s onboard accelerometer can make objects “move” around the screen, responding in real time to the way the user tilts the phone. Recipe 1-4 builds an animated butterfly that users can slide across the screen.

The secret to make this work lies in adding what a “physics timer” to the program. Instead of responding directly to changes in acceleration, the way Recipe 1-3 did, the accelerometer callback measures the current forces. It’s up to the timer routine to apply those forces to the butterfly over time by changing its frame. Here are some key points to keep in mind:

  • As long as the direction of force remains the same, the butterfly accelerates. Its velocity increases, scaled according to the degree of acceleration force in the X or Y direction.
  • The tick routine, called by the timer, moves the butterfly by adding the velocity vector to the butterfly’s origin.
  • The butterfly’s range is bounded. So when it hits an edge, it stops moving in that direction. This keeps the butterfly onscreen at all times. The tick method checks for boundary conditions. For example, if the butterfly hits a vertical edge, it can still move horizontally.
  • The butterfly reorients itself so it always falling “down.” This happens by applying a simple rotation transform in the tick method. Be careful when using transforms in addition to frame or center offsets. Always reset the math before applying offsets, and then reapply any angular changes. Failing to do so may cause your frames to zoom, shrink, or skew unexpectedly.

Recipe 1-4. Sliding an Onscreen Object Based on Accelerometer Feedback

- (void)accelerometer:(UIAccelerometer *)accelerometer
    didAccelerate:(UIAcceleration *)acceleration
    // Extract the acceleration components
    float xx = -acceleration.x;
    float yy = acceleration.y;

    // Store the most recent angular offset
    mostRecentAngle = atan2(yy, xx);

    // Has the direction changed?
    float accelDirX = SIGN(xvelocity) * -1.0f;
    float newDirX = SIGN(xx);
    float accelDirY = SIGN(yvelocity) * -1.0f;
    float newDirY = SIGN(yy);

    // Accelerate. To increase viscosity lower the additive value
    if (accelDirX == newDirX) xaccel =
        (abs(xaccel) + 0.85f) * SIGN(xaccel);
    if (accelDirY == newDirY) yaccel =
        (abs(yaccel) + 0.85f) * SIGN(yaccel);

    // Apply acceleration changes to the current velocity
    xvelocity = -xaccel * xx;
    yvelocity = -yaccel * yy;

- (void) tick
    // Reset the transform before changing position
    butterfly.transform = CGAffineTransformIdentity;

    // Move the butterfly according to the current velocity vector
    CGRect rect = CGRectOffset(butterfly.frame, xvelocity, 0.0f);
    if (CGRectContainsRect(self.view.bounds, rect))
        butterfly.frame = rect;

    rect = CGRectOffset(butterfly.frame, 0.0f, yvelocity);
    if (CGRectContainsRect(self.view.bounds, rect))
        butterfly.frame = rect;

    // Rotate the butterfly independently of position
    butterfly.transform =
        CGAffineTransformMakeRotation(mostRecentAngle + M_PI_2);

- (void) initButterfly
    CGSize size;

    // Load the animation cells
    NSMutableArray *butterflies = [NSMutableArray array];
    for (int i = 1; i <= 17; i++)
        NSString *fileName = [NSString stringWithFormat:@"bf_%d.png", i];
        UIImage *image = [UIImage imageNamed:fileName];
        size = image.size;
        [butterflies addObject:image];

    // Begin the animation
    butterfly = [[UIImageView alloc]
    [butterfly setAnimationImages:butterflies];
    butterfly.animationDuration = 0.75f;
    [butterfly startAnimating];

    // Set the butterfly's initial speed and acceleration
    xaccel = 2.0f;
    yaccel = 2.0f;
    xvelocity = 0.0f;
    yvelocity = 0.0f;

    // Add the butterfly
    butterfly.center = RECTCENTER(self.view.bounds);
    [self.view addSubview:butterfly];

    // Activate the accelerometer
    [[UIAccelerometer sharedAccelerometer] setDelegate:self];

    // Start the physics timer
    [NSTimer scheduledTimerWithTimeInterval: 0.03f
        target: self selector: @selector(tick)
        userInfo: nil repeats: YES];
  • + Share This
  • 🔖 Save To Your Account