So Apple released a new beta of iPhone OS 3.0 today. It’s the fifth beta and we’re getting closer to final release of 3.0. There were very few changes or additions to the API so it’s not likely we’ll see more changes at all. In February I published a quite lengthy solution  on how to rotate images without rotating the entire application where I use shouldAutorotateToInterfaceOrientation.  Recently I wrote a post about the changes made to shouldAutorotateToInterfaceOrientation which in my opinion breaks important functionality. As promised here’s how you can replace shouldAutorotateToInterfaceOrientation using the accelerometer.

*UPDATE*
Kyle was friendly enough to post a simple solution to how you can listed to the shouldAutorotateToInterfaceOrientation event. I’ve updated my previous post with his solution (or see his comment below): breaking changes to shouldAutorotateToInterfaceOrientation.

The application is called DeviceOrientation and is just a proof-of-concept, not a full replacement for shouldAutorotateToInterfaceOrientation. Unfortunately the accelerometer cannot be simulated in the simulator, you will have to compile and run it on your iPhone to test. Here’s a dirty clip I recorded with the iSight camera in my iMac, it’s harder than you think to record a clip sitting behind the device and the screen itself adds a nasty glare in the iPhone. At least it’s pretty short, enjoy:


Warning math and code head!

To calculate the angle from the accelerator you use atan2. Instead of trying to explain atan2 myself, here’s wikipedias short explenation:

In trigonometry, the two-argument function atan2 is a variation of the arctangent function. For any real arguments x and y not both equal to zero, atan2(yx) is the angle in radians between the positive x-axis of a plane and the point given by the coordinates (xy) on it. The angle is positive for counter-clockwise angles (upper half-plane, y > 0), and negative for clockwise angles (lower half-plane, y < 0).

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
	// Get the current device angle
	float xx = -[acceleration x];
	float yy = [acceleration y];
	float angle = atan2(yy, xx);
}

I put some effort into creating a, hopefully, easy to understand visualization of how angles relate to the devices actual orientation. It’s easier if you hold the phone in front of you when you look at it and rotate it and compare it to the image:

angles-and-device-orientation
So many iPhones on a single picture, awesome!

Knowing the angles of each orientation is obvious that between each orientation there difference is 1.5 and halfway between each orientation the offset is then 0.75. The following code changes the label text to the interface orientation but only when the orientation changes from one to another:

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
	// Get the current device angle
	float xx = -[acceleration x];
	float yy = [acceleration y];
	float angle = atan2(yy, xx); 
 
	// Add 1.5 to the angle to keep the label constantly horizontal to the viewer.
	[interfaceOrientationLabel setTransform:CGAffineTransformMakeRotation(angle+1.5)]; 
 
	// Read my blog for more details on the angles. It should be obvious that you
	// could fire a custom shouldAutorotateToInterfaceOrientation-event here.
	if(angle >= -2.25 && angle <= -0.75)
	{
		if(deviceOrientation != UIInterfaceOrientationPortrait)
		{
			deviceOrientation = UIInterfaceOrientationPortrait;
			[interfaceOrientationLabel setText:@"UIInterfaceOrientationPortrait"];
		}
	}
	else if(angle >= -0.75 && angle <= 0.75)
	{
		if(deviceOrientation != UIInterfaceOrientationLandscapeRight)
		{
			deviceOrientation = UIInterfaceOrientationLandscapeRight;
			[interfaceOrientationLabel setText:@"UIInterfaceOrientationLandscapeRight"];
		}
	}
	else if(angle >= 0.75 && angle <= 2.25)
	{
		if(deviceOrientation != UIInterfaceOrientationPortraitUpsideDown)
		{
			deviceOrientation = UIInterfaceOrientationPortraitUpsideDown;
			[interfaceOrientationLabel setText:@"UIInterfaceOrientationPortraitUpsideDown"];
		}
	}
	else if(angle <= -2.25 || angle >= 2.25)
	{
		if(deviceOrientation != UIInterfaceOrientationLandscapeLeft)
		{
			deviceOrientation = UIInterfaceOrientationLandscapeLeft;
			[interfaceOrientationLabel setText:@"UIInterfaceOrientationLandscapeLeft"];
		}
	}
}

If you want a replacement to shouldAutorotateToInterfaceOrientation you can use this code and instead of changing the text of a label, you fire an event.

Download the code

As I wrote above (in case you skipped directly to the code), this application must be run on the device, the simulator unfortunately doesn’t simulate the accelerometer. The application will run, but it won’t do you much. As always the code is free to use any way you like. All code on this blog is free to use, even if the comments in some files say different.

Download DeviceOrientation demo application (full source included)

Related posts