Code, Code, Revolution!
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:
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(y, x) is the angle in radians between the positive x-axis of a plane and the point given by the coordinates (x, y) 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:

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.
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)
With this blog I try to provide useful tips and solutions for programming .NET, Objective-C and more. My name is Björn Sållarp, and I love writing code.
iphone architect
May 11th, 2009 at 12:44 pm
here is a tutorial about iphone accelerometer simulation without iphone
http://www.iphonearch.com/topic/6/iphone-accelerometer-simulator/
viktor
May 14th, 2009 at 8:15 am
Nice to see you in so deep focus, (and also nice floating, change the text to a boat)
Adrian Oldham
May 18th, 2009 at 12:16 pm
Correct me if I’m wrong, but I would think that the first “if” statement should have a value of -0.75 rather than -0.25.
ie: if(angle >= -2.25 && angle <= -0.75)
Kyle
May 19th, 2009 at 5:19 am
You should be able to listen for the system “UIDeviceOrientationDidChangeNotification” event and use UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation]; to get the orientation changes in the notification function
Kyle
May 19th, 2009 at 5:24 am
Not sure if I am allow to post code but here’s some code for those who are curious:
-(void) viewWillAppear: (BOOL) animated{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(receivedRotate:)
name: UIDeviceOrientationDidChangeNotification
object: nil];
…
}
-(void) receivedRotate: (NSNotification*) notification
{
UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
…
}
}
-(void) viewWillDisappear: (BOOL) animated{
[[NSNotificationCenter defaultCenter] removeObserver: self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
…
}
Björn Sållarp
May 19th, 2009 at 7:17 am
Hey Kyle,
Awesome code! That does give access to the shouldAutorotateToInterfaceOrientation event! Thanks a lot. I’ll update my other post later with your solution.
Björn Sållarp
May 19th, 2009 at 7:18 am
Adrian,
Very true, I’ve updated the post. Thanks!
OS3.0のshouldAutorotateTo-InterfaceOrientationがおかしい件 | iphoneアプリで稼げるのか
June 18th, 2009 at 5:43 pm
[...] 似たような現象に遭遇してる人がいたので、めちゃめちゃ参考にしてます。 iPhone OS 3.0 – Breaking changes to shouldAutorotateToInterfaceOrientation | blog.sallarp.com Find the device orientation using the accelerometer | blog.sallarp.com [...]
Joel Morris
June 22nd, 2009 at 7:05 am
Also, the file which defines atan2() also has a bunch of constants defined like M_PI M_PI_2 (pi/2) and M_PI_4 (pi/4) I think your code could be a tiny bit more accurate if you used these instead.
Bo Gehring
October 2nd, 2009 at 2:00 pm
This is a great blog! A question… these solutions appear to be for yaw only. Is it possible to derive pitch and roll too? That would fully describe the device’s orientation.
Shawn Brinkman
February 5th, 2010 at 9:24 pm
I get this build error when tring to run the project.
error: There is no SDK with the name or path ‘iphoneos2.0′
Can you please help me .
Thank you
Shawn
Lars
February 9th, 2010 at 1:57 pm
Hei Björn, Takk skal du ha! (15 months in Norway, but this is pretty much all I can say in Norwegian, sorry
Thank you very much indeed for your excellent sample code and all the great examples which are extremely useful for iPhone development beginners like myself.
As I can see from your sample code, the orientation determination always starts when the first view is already added to the window as a subview. Is there a way of starting earlier? Actually I do not like Apple’s approach to start all applications in Portrait Orientation first and only then switch to Landscape Orientation if necessary.
If I hold my iPhone already in Landscape Orientation and start an app or safari (yes, I know, I shouldn’t), it will show Portrait Orientation first and switch directly to Landscape Orientation afterwards. I would like to prevent this animated orientation switch entirely and start right away with the “correct” orientation of the app accordingly to the device’s physical orientation (yes, I saw your discussion on flipping the whole app’s or just the view orientation of the image view).
This is a minor issue with the iPhone since the UI of the springboard is not (yet) available in landscape orientation anyway, but rather soon it will be more important, because – even Apple admits – one cannot predict the “user preferred orientation” anymore when it comes to the iPad.
So, can I already start with this accelerometer determination at the point of “applicationDidFinishLaunching”? I would like to determine the necessary view orientation first (accordingly to the physical orientation of the device) and only then add the respective (portrait or landscape) view controller and view as a subview to the window.
How would I add a view controller dynamically then (sorry, I am still a beginner)?
Since I do not love too much this animated rotation of the views, I would rather like to use something which is shown in Apple’s AlternateViews example and to have this quick and neat cross-dissolve effect instead.
Thanks again for your helpful blog and those great insights.
Kind regards from Berlin, Lars
Lars
February 9th, 2010 at 2:51 pm
Aha, now after having a closer look at your “ScrollViewImageGallery” example, I see how to add a view controller dynamically! Thank you very much for all the comments, very useful.
Kind regards, Lars