 # Optimizing Device-Specific iOS Development

• Print
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;

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

// Begin the animation
butterfly = [[UIImageView alloc]
initWithFrame:(CGRect){.size=size}];
[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;

butterfly.center = RECTCENTER(self.view.bounds);

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

// Start the physics timer
[NSTimer scheduledTimerWithTimeInterval: 0.03f
target: self selector: @selector(tick)
userInfo: nil repeats: YES];
}```