Affine transforms enable you to change an
object’s geometry by mapping that object from one view coordinate
system into another. The iPhone SDK fully supports standard affine 2D
transforms. With them, you can scale, translate, rotate, and skew your
views however your heart desires and your application demands.
Transforms are defined in Core Graphics and consist of calls such as CGAffineTransformMakeRotation and CGAffineTransformScale. These build and modify the 3-by-3 transform matrices. Once built, use UIView’s setTransform call to apply 2D affine transformations to UIView objects.
Recipe 1 demonstrates how to build and apply an affine transform of a UIView. To create the sample, I kept things simple. I build an NSTimer
that ticks every 1/30th of a second. On ticking, it rotates a view by
1% of pi and scales over a cosine curve. I use the cosine’s absolute
value for two reasons. It keeps the view visible at all times, and it
provides a nice bounce effect when the scaling changes direction. This
produces a rotating bounce animation.
This is one of those samples that it’s best to build and view as you read through the code. You are better able to see how the handleTimer: method correlates to the visual effects you’re looking at.
Note
This recipe uses the standard C math library, which provides both the cosine function and the M_PI constant.
Recipe 1. Example of an Affine Transform of a UIView
#import <math.h> #define BARBUTTON(TITLE, SELECTOR) [[[UIBarButtonItem alloc] initWithTitle:TITLE style:UIBarButtonItemStylePlain target:self action:SELECTOR] autorelease]
@interface TestBedViewController : UIViewController { NSTimer *timer; int theta; } @end
@implementation TestBedViewController - (void) move: (NSTimer *) aTimer { // Rotate each iteration by 1% of PI CGFloat angle = theta * (M_PI / 100.0f); CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
// Theta ranges between 0% and 199% of PI, i.e. between 0 and 2*PI theta = (theta + 1) % 200;
// For fun, scale by the absolute value of the cosine float degree = cos(angle); if (degree < 0.0) degree *= -1.0f; degree += 0.5f;
// Create add scaling to the rotation transform CGAffineTransform scaled = CGAffineTransformScale(transform, degree, degree);
// Apply the affine transform [[self.view viewWithTag:999] setTransform:scaled]; }
- (void) start: (id) sender { // The timer is automatically retained by the runloop // You can start and stop it without being the owner // or messing with its retain count. timer = [NSTimer scheduledTimerWithTimeInterval:0.03f target:self @selector(move:selector:) userInfo:nil repeats:YES]; [self move:nil]; self.navigationItem.rightBarButtonItem = BARBUTTON(@"Stop", @selector(stop:)); }
- (void) stop: (id) sender { [timer invalidate]; timer = nil; self.navigationItem.rightBarButtonItem = BARBUTTON(@"Start", @selector(start:)); }
- (void) viewDidLoad { self.navigationItem.rightBarButtonItem = BARBUTTON(@"Start", @selector(start:)); UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"BflyCircle.png"]]; imgView.tag = 999; imgView.center = CGPointMake(160.0f, 143.0f); [self.view addSubview:imgView]; [imgView release];
timer = nil; theta = 0; } @end
|
Centering Landscape Views
Use
the same affine transform approach to center landscape-oriented views.
This snippet creates a 480-by-320 pixel view, centers it at [160, 240]
(using portrait view coordinates), and then rotates it into place. Half
of pi corresponds to 90 degrees, creating a landscaperight rotation.
Centering keeps the entire view onscreen. All subviews, including text
fields, labels, switches, and so on rotate into place along with the
parent view.
#define PI 3.141592f
- (void)loadView
{
contentView = [[UIView alloc] initWithFrame:
CGRectMake(0.0f, 0.0f, 480.0f, 320.0f)];
[contentView setCenter:CGPointMake(160.0f, 240.0f)];
[contentView setBackgroundColor:[UIColor blackColor]];
[contentView setTransform:CGAffineTransformMakeRotation(PI/2.0f)];
self.view = contentView;
[contentView release];
}
For the most part, it’s far easier using UIViewControllers
to work with reorientation events than manually rotating and presenting
views. Additionally, manual view rotation does not change the status
bar orientation nor the keyboard orientation.