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

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! :)

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

auto-rotation in iOS5/6?

I updated to Xcode 4.5 and am working with iOS6--a mistake I will definitely not make next time there's an update; it's been sort of nightmarish for somebody so new to iOS--and I've just noticed an app I'm working on is autorotating. I never noticed it autorotatin before the update, but it's also possible I just didn't rotate the phone while testing, so I can't be sure. I've added the following code to the main UIViewController and it's still rotating:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
Is this the right way to disable autorotation? If it is, then maybe there's some change in iOS6 and I'll have to wait until the full release to find out. But if I've gotten it wrong, what code should I use instead?
Thanks, as always, for your help.
EDIT: Here's the code I changed it to, but it's still rotating. Have I gotten it wrong?
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait)
{
return YES;
}
else
{
return NO;
}
}
that is because there was never a success. You should choose one of the orientations.
Hold command and click on UIInterfaceOrientation you will see an enumeration of the possible options.
then you can test against those to decide your YES Scenario.
I may have originally misunderstood your problem. It seems that you may have been saying that your app is allowing rotation. but the code should disallow that.
I was thinking you were saying it was still firing the code. Trying to find a Yes
One thing to think about. is there may be more than one view controller available. perhaps your code is not being hit.
A couple of possible issues for this.
Your code is not even being used. because the view is being allocated as UIViewController as opposed to your custom view controller.
You code is being used but that View controller is not the one being asked about the Orientation. therefore that specific code is not being hit.
A bad build keeps putting the wrong assemblies onto the device.
Your solutions can be as follows.
Ensure your code is the one being allocated. Either there is a direct alloc on your custom class. or the xib file is inflating it. Check out the Identity Inspector when you have your xib file open. select your View Controller and ensure that custom class is set to your class type
Look at the hierarchy. what other view controllers are there. Perhaps one of those are telling the app it can autorotate to any orientation.
Find your "DerivedData" folder and remove it entirely. Sometimes this works from the organizer. other times you need to delete directly off the disk. Then clean and build again.
Also another solution could be as simple as setting the settings in the Project file.
Select your project file from the file browser and you will see the iPad and iPod settings in the summary. You can "UnPress" buttons for the orientations that you want to disallow. and any view controllers that you do not otherwise code orientation into. will use these by default.
My apologies for the confusion.
Update
I commonly use this code to handle my autorotation.
It not only differentiates the ipad from the other ios devices, but it also forwards the request onto presented controllers so a view that is shown modal may respond how it wants.
Orientation is a pain when you dont understand it :)
// Detect iPad
#define IS_IPAD() ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)] ? \
[[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad : NO)
// Set preferred orientation for initial display
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
if (IS_IPAD()){
return UIInterfaceOrientationLandscapeRight;
}
else {
return UIInterfaceOrientationPortrait;
}
}
// Return list of supported orientations.
- (NSUInteger)supportedInterfaceOrientations{
if (self.presentedViewController != nil){
return [self.presentedViewController supportedInterfaceOrientations];
}
else {
if (IS_IPAD()){
return UIInterfaceOrientationMaskLandscapeRight;
}
else {
return UIInterfaceOrientationMaskAll;
}
}
}
// Determine iOS 6 Autorotation.
- (BOOL)shouldAutorotate{
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
// Return yes to allow the device to load initially.
if (orientation == UIDeviceOrientationUnknown) return YES;
// Pass iOS 6 Request for orientation on to iOS 5 code. (backwards compatible)
BOOL result = [self shouldAutorotateToInterfaceOrientation:orientation];
return result;
}
// handle iOS 5 Orientation as normal
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
// Return YES for supported orientations
if (self.presentedViewController != nil){
return [self.presentedViewController shouldAutorotate];
}
else {
if (IS_IPAD()){
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
else {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
}
}
Rotation APIs have changed in iOS6. The new API's are apparently supposed to be opt in however they seem to be enabled for all debug builds on simulator or device. To register for the new API calls throw something like this in your APP Delegates didFinishLoading method.
//Register for new API rotation calls
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"UIApplicationSupportedInterfaceOrientationsIsEnabled"];
At the heart of the rotation changes are two methods (theres a third but Im still figuring this out myself)
- (BOOL)shouldAutorotate
- (NSUInteger)supportedInterfaceOrientations
You need to override these methods in your windows rootViewController. This means you need to subclass UINavigationController or UITabBarController if either is your root controller (this seems bizarre to me, but Apple says Jump).
Now if all you want to do is keep your app in portrait implement the two methods and you're golden.
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Note that apple has further added to the confusion by adding interface orientation masks, ie. UIInterfaceOrientationMaskPortrait != UIInterfaceOrientationPortrait. If you return UIInterfaceOrientationPortrait instead the behaviour will be different. Also you can combine masks the same way you combine orientations so if you wanted to support both portrait orientations you could use.
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
That should work for forcing a portrait orientation. Now if you if you want do do something like allow a child controller to use a different orientation I have no clue.
A very simple way to handle autorotation in both iOS6 and iOS5 is to use supportedInterfaceOrientations & shouldAutorotateToInterfaceOrientation. There are some macros to make it just a line of code. UIInterfaceOrientationIsLandscape & UIInterfaceOrientationMaskLandscape.
I discovered UIInterfaceOrientationMaskLandscape & UIInterfaceOrientationMaskPortrait by auto-completion in xCode. It is not in the Apple docs about autorotation.
Add this code block to your root ViewController to force it to support only landscape mode.
//iOS6 code to support orientations
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskLandscape);
}
//iOS5 code to support orientations
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
return (UIInterfaceOrientationIsLandscape(orientation));
}
For iOS6 you can use the following to detect orientation:
UIInterfaceOrientationMaskLandscape
UIInterfaceOrientationMaskPortrait
UIInterfaceOrientationMaskLandscapeRight
UIInterfaceOrientationMaskLandscapeLeft
UIInterfaceOrientationMaskPortraitUpsideDown
UIInterfaceOrientationMaskAllButUpsideDown
UIInterfaceOrientationMaskAll
For iOS5 and below you can use the following to detect orientation:
UIInterfaceOrientationIsLandscape (A macro)
UIInterfaceOrientationIsPortrait
UIInterfaceOrientationIsLandscapeRight
UIInterfaceOrientationIsLandscapeLeft
UIInterfaceOrientationIsPortraitUpsideDown

issue about willAnimateRotate and didRotateFrom when doing an rotation

What I am having so far is
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
NSLog(#"willAnimateRotationToInterfaceOrientation");
if(toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"PortraitUpsideDown");
// Do method A
} else {
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method B
}
}
and
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
NSLog(#"didRotateFromInterfaceOrientation");
if( fromInterfaceOrientation == UIInterfaceOrientationPortrait || fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown){
NSLog(#"OrientationPortrait or PortraitUpsideDown");
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method B
} else {
NSLog(#"From else");
[[self.view subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
// Do method A
}
}
My logic is after hitting the RUN from xcode, willAnimateRotationToInterfaceOrientation: is going to be called because I set Supported Device Orientation to be UpsideDown from Summary of MyApp.xcodeproj. Moreover, I also think that didRotateFromInterfaceOrientation: should not be called because we have just started the app yet. It means there are no previous states at all.
Unfortunately, this is what I got after doing the debugger
2012-02-11 12:04:08.776 MyApp[7505:10703] willAnimateRotationToInterfaceOrientation :
2012-02-11 12:04:08.776 MyApp[7505:10703] PortraitUpsideDown
2012-02-11 12:04:08.778 MyApp[7505:10703] didRotateFromInterfaceOrientation :
2012-02-11 12:04:08.779 MyApp[7505:10703] OrientationPortrait or
PortraitUpsideDown
I am getting lost now. Does anyone have any ideas about the issue, please help. Thanks.
I have had some issues with rotation, similar to yours. The short version is that the will and did methods are not 100% reliable. You should try rapidly rotating back and forth and see what happens. It could be because I am pretty new, or it could be an actual bug. What I did was, in didRotateFromInterfaceOrientation:, check the current orientation and act accordingly:
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) {
// do your portrait stuff
} else {
// do your landscape stuff
}
Before I found the UIInterfaceOrientationIsPortait test I was actually checking the frame to see if self.view.frame.size.width > self.view.frame.size.height, and that is also reliable but kind of ugly.
Either way is reliable. I have a couple apps in the store, QPalettes and QColor, that do rotation on a bunch of user-created onscreen elements. What I had to do is store the dimensions, and after didRotateFromInterfaceOrientation I check the new dimensions and if they are different, re-draw everything onscreen. Contact me if you'd like a promo code to check out the rotation in either app (they rotate the same - QColor is more complex since it also draws intersections).
Enjoy,
Damien

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].