how do I detect the iPhone orientation before rotating - iphone

In my program I'm moving things based on rotation, but I'm not rotating the entire view. I'm Using :
static UIDeviceOrientation previousOrientation = UIDeviceOrientationPortrait;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
- (void) didRotate:(NSNotification *)notification{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[self doRotationStuff:orientation: previousOrientation];
previousOrientation = orientation;
}
This works as long as, when the program is launched, the device orientation is portrait, but not if the initial orientation is landscape or upside down, because [self doRotationStuff] makes changes relative to the difference from the previous orientation.
Is there a way to detect the orientation either at launch, or right before the device is rotated?

Depending on your circumstances, a simpler option may be the interfaceOrientation property of the UIViewController class. This is correct before a rotation.

Updated:
So, from the comment discussion, it appears that you can't rely on [UIDevice currentDevice].orientation until the orientation actually changes for the first time. If so, you could probably hack it by getting raw accelerometer readings.
#define kUpdateFrequency 30 // Hz
#define kUpdateCount 15 // So we init after half a second
#define kFilteringFactor (1.0f / kUpdateCount)
- (void)applicationDidFinishLaunching:(UIApplication *)app
{
[UIAccelerometer sharedAccelerometer].updateInterval = (1.0 / kUpdateFrequency);
[UIAccelerometer sharedAccelerometer].delegate = self;
accelerometerCounter = 0;
...
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)accel
{
// Average out the first kUpdateCount readings
// acceleration_[xyz] are ivars typed float
acceleration_x = (float)accel.x * kFilteringFactor + acceleration_x * (1.0f - kFilteringFactor);
acceleration_y = (float)accel.y * kFilteringFactor + acceleration_y * (1.0f - kFilteringFactor);
acceleration_z = (float)accel.z * kFilteringFactor + acceleration_z * (1.0f - kFilteringFactor);
accelerometerCounter++;
if (accelerometerCounter == kUpdateCount)
{
[self initOrientation];
[UIAccelerometer sharedAccelerometer].delegate = nil;
}
}
- (void)initOrientation
{
// Figure out orientation from acceleration_[xyz] and set up your UI...
}
Original response:
Does [UIDevice currentDevice].orientation return the correct orientation during applicationDidFinishLaunching:? If so, you can set up your initial UI according to that orientation.
If that property doesn't get set until some later time, you might try experimenting with performSelector:afterDelay: to initialize the UI after a small delay.
This code sample is from Kendall's answer below, added here for completeness:
[self performSelector:#selector(getOriented) withObject:nil afterDelay:0.0f];
I'm not sure if a zero-second delay is sufficient -- this means the code for getOriented will run during the first pass through the event run loop. You may need to wait longer for the accelerometer readings to register on UIDevice.

Mort, these answers seem somewhat of a red herring; I can't see why you can't use the following built-in method for a UIViewController class:
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {}
This method gets called automatically after a rotation has occurred (rather than with shouldAutorotateToInterfaceOrientation which only tells you it's about to happen). Handily, the variable 'fromInterfaceOrientation' contains the previous orientation. As the documentation also says, you can assume that the interfaceOrientation property of the view has already been set to the new orientation, so you then have one method with access to the old orientation and the new!
If I've missed something and you've already dismissed being able to use this method, my apologies! It just seems odd that you're creating and storing a variable for the "previous orientation" when it's provided for free in the above method.
Hope that helps!

Use this for the orientation of the UI if you need to determine what way are you pointing.
Not 100% sure this is right but going off the top of my head:
[[[UIApplication sharedApplication] statusBar] orientation]

Here's one way to get the orientation when the app first loads and the UIDeviceOrientation is set to UIDeviceOrientationUnknown. You can look at the transform property of the rotated view.
if(toInterface == UIDeviceOrientationUnknown) {
CGAffineTransform trans = navigationController.view.transform;
if(trans.b == 1 && trans.c == -1)
toInterface = UIDeviceOrientationLandscapeLeft;
else if(trans.b == -1 && trans.c == 1)
toInterface = UIDeviceOrientationLandscapeRight;
else if(trans.a == -1 && trans.d == -1)
toInterface = UIDeviceOrientationPortraitUpsideDown;
else
toInterface = UIDeviceOrientationPortrait;
}

A more complete example on how to obtain device orientation from accelerator readings can be found here
As the solution relies on accelerator readings, it wouldn't work on the simulator, so you'll have to work on the device... still looking myself for a solution that works on the simulator.

In response to your comment, I thought I could better put code here than in a comment (though really Daniel deserves credit here):
in applicationDidFinishLaunching:
[self performSelector:#selector(getOriented) withObject:nil afterDelay:0.0f];
Then you just need the method to call:
- (void) getOriented
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
// save orientation somewhere
}

Related

How to change the orientation of the app without changing the device orientation in iphone app

I want to change the orientation of the app without changing the device orientation in iphone app.
I want to change my view from portraid mode to landscap mode programmatically.
And also want to know that will this be accepted by the apple store or not ?
Thanks
Now I got the solution from other that is as follow
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
But now want to know is this accepted by apple app store or not ?
thanks
use this line for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
and also when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
// return NO;
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
i hope this help you..
:)
Add a class variable
Bool isInLandsCapeOrientation;
in viewDidLoad
set this flag to
isInLandsCapeOrientation = false;
Add the following function
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (!isInLandsCapeOrientation) {
return (UIInterfaceOrientationIsPortrait(interfaceOrientation));
}else {
return (UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
}
To changing orientation from portrait to landscape, let it happens on a button action
- (IBAction)changeOrientationButtonPressed:(UIButton *)sender
{
isInLandsCapeOrientation = true;
UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
}
This works fine for me.
To change Orientation portraid mode to landscap mode use this code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
use this code for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
If you want to change the particular view only in landscape..then u can try the following in its viewDidLoad
float angle = M_PI / 2;
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
[ [self.view] setTransform:transform];
The documentation describes the orientation property as being read-only, so if it works, I'm not sure you can rely on it working in the future (unless Apple does the smart thing and changes this; forcing orientation changes, regardless of how the user is currently holding their device, is such an obvious functional need).
As an alternative, the following code inserted in viewDidLoad will successfully (and somewhat curiously) force orientation (assuming you've already modified you shouldAutorotateToInterfaceOrientation ):
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]))
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
}
Clearly, this does it if the user is currently holding their device in portrait orientation (and thus presumably your shouldAutorotateToInterfaceOrientation is set up for landscape only and this routine will shift it to landscape if the user's holding their device in portrait mode). You'd simply swap the UIDeviceOrientationIsPortrait with UIDeviceOrientationIsLandscape if your shouldAutorotateToInterfaceOirentation is set up for portrait only.
For some reason, removing the view from the main window and then re-adding it forces it to query shouldAutorotateToInterfaceOrientation and set the orientation correctly. Given that this isn't an Apple approved approach, maybe one should refrain from using it, but it works for me. Your mileage may vary. But this also refers to other techniques, too. Check
SO discussion

[UIDevice currentDevice].orientation == UIDeviceOrientationUnknown following UINavigationController push

This is an observation more than anything, because I appear to have found a work-around...
The code below fails to work when it's in a controller which has been pushed onto a UINavigationController stack. In this situation [UIDevice currentDevice].orientation consistently returns UIDeviceOrientationUnknown.
-(void)layoutAccordingToOrientation {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
NSLog(#"layoutAccordingToOrientation/orientation==%i", orientation);
if (UIInterfaceOrientationIsLandscape(orientation)) {
:
_detailToolbar.frame = CGRectMake(0, 660, 1024, 44);
} else {
:
_detailToolbar.frame = CGRectMake(0, 916, 768, 44);
}
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self layoutAccordingToOrientation];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
The following call has been made:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
To work around this UIDeviceOrientationUnknown-problem, I now use the following instead:
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(orientation)) {
etc.
} else {
etc.
}
... which works every time.
Still, I fail to see why the first variation would not work in the context of a pushed view controller. Ideas anyone? Is it simply a bug?
I could think of a couple of things here, but I'm not sure they're what you're looking for...
First, when the app is launched, usually the device orientation returns UIDeviceOrientationUnknown for performance reasons. One thing you could try is to layout your subviews inside a .nib or in your Storyboard, with the correct autoresizing options, and let iOS do its thing.
Secondly, if you're like me, maybe you're testing on a device that's right beside your computer, laying on a table. Well, in that case, you are not going to get the UIDeviceOrientationUnknown thing. You should actually test for UIDeviceOrientationIsValidInterfaceOrientation([[UIDevice currentDevice] orientation]). If the device is laying down on its back, for example, it returns yes to that statement. This also means that when you use UIInterfaceOrientationIsLandscape(orientation), you're getting the right result for the wrong reason. The UIInterfaceOrientationIsLandscape is returning false not because the device's orientation is UIDeviceOrientationPortrait, but because it's invalid.
Not so sure if this helped, but it took me a while to figure that out and I thought it would be nice to share this info! :)

Is there a way to determine device orientation if view is loaded before [window makeKeyAndVisible] is called?

View of my UIViewController is loaded from a NIB before [window makeKeyAndVisible] is called in application:didFinishLaunchingWithOptions: so I cannot get the correct device orientation - i.e. self.interfaceOrientation, [[UIApplication sharedApplication] statusBarOrientation] and [[UIDevice currentDevice] orientation] all give me false orientation info that I'm in portrait mode (iPad's default mode, I think).
Is there any way to work around that problem? Please don't say I need to use accelerometer ;)
EDIT: for whose who are interested on getting orientation info based on accelerometer reading: http://web.archive.org/web/20091111033649/http://blog.sallarp.com/iphone-accelerometer-device-orientation
it appears that you can't rely on [UIDevice currentDevice].orientation until the orientation actually changes for the first time. If so, you could probably hack it by getting raw accelerometer readings.
#define kUpdateFrequency 30 // Hz
#define kUpdateCount 15 // So we init after half a second
#define kFilteringFactor (1.0f / kUpdateCount)
- (void)applicationDidFinishLaunching:(UIApplication *)app
{
[UIAccelerometer sharedAccelerometer].updateInterval = (1.0 / kUpdateFrequency);
[UIAccelerometer sharedAccelerometer].delegate = self;
accelerometerCounter = 0;
...
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)accel
{
// Average out the first kUpdateCount readings
// acceleration_[xyz] are ivars typed float
acceleration_x = (float)accel.x * kFilteringFactor + acceleration_x * (1.0f - kFilteringFactor);
acceleration_y = (float)accel.y * kFilteringFactor + acceleration_y * (1.0f - kFilteringFactor);
acceleration_z = (float)accel.z * kFilteringFactor + acceleration_z * (1.0f - kFilteringFactor);
accelerometerCounter++;
if (accelerometerCounter == kUpdateCount)
{
[self initOrientation];
[UIAccelerometer sharedAccelerometer].delegate = nil;
}
}
- (void)initOrientation
{
// Figure out orientation from acceleration_[xyz] and set up your UI...
}
Original response:
Does [UIDevice currentDevice].orientation return the correct orientation during applicationDidFinishLaunching:? If so, you can set up your initial UI according to that orientation.
If that property doesn't get set until some later time, you might try experimenting with performSelector:afterDelay: to initialize the UI after a small delay.
This code sample is from Kendall's answer below, added here for completeness:
[self performSelector:#selector(getOriented) withObject:nil afterDelay:0.0f];
I'm not sure if a zero-second delay is sufficient -- this means the code for getOriented will run during the first pass through the event run loop. You may need to wait longer for the accelerometer readings to register on UIDevice.
I found that the only workable solution was to implement layoutSubviews for the main view and do the reorientation of the layout there (via a call back into my view controller).
Once you're at that point UIViewController.interfaceOrientation appears to be reliable.

How can I make a custom orientation lock action?

I would like to make a custom orientation lock button for a reader app of mine, and I was thinking it wouldn't be too bad to whip up, but alas I am the one getting whipped.
To start off I do have this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
And then I was thinking that I could handle the actual locking in an action method like this:
- (IBAction) screenLock:(id)sender{
if([UIDevice currentDevice].orientation == UIDeviceOrientationPortrait){
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}else{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
}
But alas, this code will not hold sway over the former that instructs the view to rotate...
Am I going about this all wrong? What is a better way to do it? I just want to have local, easy way to have my users lock the orientation of their screen. I guess it would be using a boolean value where they hit a button to lock and then hit again to unlock...
Thoughts?
Thanks!!
shouldAutorotateToInterfaceOrientation ripples up your view hierarchy so your logic needs to be put into your app delegate (or as the most senior ViewController that might return YES). Put a BOOL property in your appDelegate and set it via your lock button (e.g. target pointers/delegates (AppDelegate)) then in your appDelegate do something like this:
#define ROTATION_MASTER_ENABLED 1
//Setting MASTER_ROTATION_LOCK_ENABLED to 0 will stop the device rotating
//Landscape UP>landscape DOWN and Portrait UP>Portrait DOWN,
//This is not generally desired or app store safe, default = 1
-(BOOL)compareOrientation:(UIInterfaceOrientation)interfaceOrientation
{
UIInterfaceOrientation actual = [[UIDevice currentDevice] orientation];
if(UIInterfaceOrientationIsLandscape(interfaceOrientation) && UIInterfaceOrientationIsLandscape(actual))return YES;
else if(UIInterfaceOrientationIsPortrait(interfaceOrientation)&& UIInterfaceOrientationIsPortrait(actual))return YES;
else return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(!MASTER_ROTATION_LOCK_ENABLED)return NO;
else if(self.rotationEnabled || [self compareOrientation:interfaceOrientation])return YES;
return NO;//self.rotationEnabled is a BOOL
}

How do I detect a rotation on the iPhone without the device autorotating?

Anyone know how to do this?
I thought this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
}
- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
}
- (void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
}
- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration {
}
may prevent the device from rotation (overriding all rotate methods of my UIViewController and not calling the superclass) but I fear it's not the UIViewController that actually performs the rotation.
My UIViewController is in a UINavigationController.
Anyone have any ideas?
Cheers,
Nick.
You can register for the notification UIDeviceOrientationDidChangeNotification (from UIDevice.h), and then when you care about orientation changes call this method:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
When you no longer care about orientation changes, call this method:
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
The notification will be sent when applicable, and you can check [UIDevice currentDevice].orientation to find out what the current orientation is.
On a side note shouldAutoRotateToInterfaceOrientation used to be called on the time for rotation in 2.x, but in 3.0 it's much less consistent. The notification mentioned in the other post is the way to go for reliable indication of rotation.
You're returning YES from shouldAutorotateToInterfaceOrientation:, which I suspect you didn't mean to do. That's the only method you need to implement to prevent the device from rotating.
As for getting the current orientation of the device, you can use [[UIDevice currentDevice] orientation].