iOS: How do I figure out the current orientation in viewWillAppear? - ios

I have a UIWebView that I would like to load different URLs depending on whether its portrait or landscape. How can I figure out what my current orientation is inside viewWillAppear?

Use UIApplication's statusBarOrientation property. You may run into problems if you use the device orientation if it is UIDeviceOrientationFaceUp or UIDeviceOrientationFaceDown.
Example
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(orientation))
{
// Do something when in landscape
}
else
{
// Do something when in portrait
}

UIDeviceOrientation getCurrentOrientation() {
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
UIDeviceOrientation currentOrientation = device.orientation;
[device endGeneratingDeviceOrientationNotifications];
return currentOrientation;
}
It's up to you to convert the UIDeviceOrientation to UIInterfaceOrientation.

When the app loads, it does not know its current orientation-
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait) {
NSLog(#"portrait");// only works after a rotation, not on loading app
}
Once you rotate the device, you get correct orientation, but when the app is loaded, without changing the orientation, it seems that using [[UIDevice currentDevice] orientation] doesn't know the current orientation.
So you need to do 2 things -
Try setting the application's accepted device orientations in the plist file
In your UIViewControllers, you will need to override the shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) method to return YES when the app should rotate:

If the is sequencing of relevant calls mean that you can't rely on interfaceOrientation having the correct value at viewWillAppear then — assuming the parent view controller rotates — the safest thing is probably self.parentViewController.interfaceOrientation. Failing that you could try making a first assumption from [[UIDevice currentDevice] orientation], which may not always be 100% on the money (e.g. if the device is lying flat on a table when your app is launched) but is likely to give you a better guess than always assuming portrait.

Related

Wrong value for statusBarOrientation on viewWillAppear

I need to change the image background of a View depending on the orientation. For this, I am using the statusBarOrientation approach in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(currentOrientation)) {
NSLog(#"PORTRAIT");
} else if (UIInterfaceOrientationIsLandscape(currentOrientation)) {
NSLog(#"LANDSCAPE");
}
}
The problem is that the console is always showing PORTRAIT, even when the iPad is held in landscape mode. The same code in viewDidAppear works correctly, but there is too late and the user can see the change of images. That makes me think that the correct state of statusBarOrientation is still not available in viewWillAppear, but I have read in some other questions that this code should work there.
Try
int type = [[UIDevice currentDevice] orientation];
if (type == 1) {
NSLog(#"portrait default");
}else if(type ==2){
NSLog(#"portrait upside");
}else if(type ==3){
NSLog(#"Landscape right");
}else if(type ==4){
NSLog(#"Landscape left");
}
You shouldn't be using the statusBarOrientation to determine the current orientation of the application. According to Apple's doc: http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html
The value of this property is a constant that indicates an orientation of the receiver's status bar. See UIInterfaceOrientation for details. Setting this property rotates the status bar to the specified orientation without animating the transition. If your application has rotatable window content, however, you should not arbitrarily set status-bar orientation using this method. The status-bar orientation set by this method does not change if the device changes orientation.
Try using the interfaceOrientation property of a UIViewController to get the orientation of the current application.
Here is a useful code snippet for logging the device's interfaceOrientation:
NSArray* orientations = #[ #"nothing", #"UIInterfaceOrientationPortrait", #"UIInterfaceOrientationPortraitUpsideDown", #"UIInterfaceOrientationLandscapeLeft", #"UIInterfaceOrientationLandscapeRight”];
NSLog(#"Current orientation := %#", orientations[self.interfaceOrientation]);
Hope this helps someone out!

UIDeviceOrientation ipad

I have the following code to fill a view according to the orientation. This always returns landscape.
- (void)setData:(BCPlaylist *)list {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown){
NSLog(#"portrait");
[self setPlaylist:list];
[self renderPlaylist];
[activity stopAnimating];
}else{
NSLog(#"landscape");
[self setPlaylist:list];
[self renderPlaylistOne];
[activity stopAnimating];
}
}
I change views correctly in - (void)animateRotation:(UIInterfaceOrientation)interfaceOrientation
duration:(NSTimeInterval)duration {
But that doesn't work when already in landscape or portrait when changing a playlist.
Instead of using [[UIDevice currentDevice] orientation] for checking orientation use statusbar orientation to get the exact orientation.
UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
To avoid a warning, use :
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
You'd always get landscape, because that's your default (from your if statement). If you walk through the debugger with a breakpoint there, you'll see that the orientation reported is that of Unknown.
In fact, your code is fine, but this is a limitation of the simulator. If you take the same code, using Device orientation and not Interface orientation, you'll get actual values if you use it on the physical device, which can be driven by the if-statement you have.

change return from shouldAutorotateToInterfaceOrientation: at runTime

I'm using shouldAutorotateToInterfaceOrientation: for respond YES to all orientations.
At runtime, in few case, i must change the return to YES only in portrait.
But if i change this return type, and my device not physicly turn, my interface orientation not change.
How can it's possible to force uiView to check the shouldAutorotateToInterfaceOrientation: and rotate the view without UIdevice rotation notification.
Thanks a lot.
Static method starting from IOS 5:
[UIViewController attemptRotationToDeviceOrientation];
Add this two lines of code:
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

how to deal with UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown?

a noob question here.
i detect the orientation with:
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
all is fine and dandy and I reposition my text fields and labels according to the reported orientation with
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown)
and else{} for everything else
the problem that i only recently discovered is when the UIDevice reports UIDeviceOrientationFaceUp or UIDeviceOrientationFaceDown. how do I deal with this situation ? how do I know whether UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown is happening in Portrait or Landscape ? I know that the device is facing up or down, but I don't know if I should reposition everything to Portrait or Landscape.
thank you!
Apple recommends against using device orientation for view layout. Instead, each view controller has an interfaceOrientation property, and UIApplication has a statusBarOrientation property, both of which will return the current interface orientation, which is suitable for view layout.
To monitor for changes, there are UIViewController methods like willRotateToInterfaceOrientation:duration: that will be called and notifications such as UIApplicationWillChangeStatusBarOrientationNotification that will be posted when an interface orientation change occurs.
A bit late, hopefully it would help some one.
For iOS 6.0 and later:
1) Set this code during your View Setup to receive rotation notification:
// Set Listener to receive orientation notifications from the device
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(detectOrientation)
name:UIDeviceOrientationDidChangeNotification
object:nil];
2) Set this code to catch the Notification after rotation:
-(void)detectOrientation{
switch ([[UIDevice currentDevice] orientation]) {
case UIDeviceOrientationFaceDown:{
NSLog(#"UIDeviceOrientationFaceDown");
break;
}
case UIDeviceOrientationFaceUp:{
NSLog(#"UIDeviceOrientationFaceUp");
break;
}
case UIDeviceOrientationLandscapeLeft:{
NSLog(#"UIDeviceOrientationLandscapeLeft");
break;
}
case UIDeviceOrientationLandscapeRight:{
NSLog(#"UIDeviceOrientationLandscapeRight");
break;
}
case UIDeviceOrientationPortrait:{
NSLog(#"UIDeviceOrientationPortrait");
break;
}
case UIDeviceOrientationPortraitUpsideDown:{
NSLog(#"UIDeviceOrientationPortraitUpsideDown");
break;
}
case UIDeviceOrientationUnknown:{
NSLog(#"UIDeviceOrientationUnknown");
break;
}
default:{
break;
}
}
}
I have the same problems with
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
This method generate notifications for 7 possible device positional states:
UIDeviceOrientationPortrait
UIDeviceOrientationPortraitUpsideDown
UIDeviceOrientationLandscapeLeft
UIDeviceOrientationLandscapeRight
UIDeviceOrientationFaceUp
UIDeviceOrientationFaceDown
UIDeviceOrientationUnknown
But I only need the first 4, well this is my approach to solve this problem (with code included):
How to handle UIDeviceOrientation for manage views layouts

Get current orientation of iPad?

In a given event handler (not the "shouldAutorotateToInterfaceOrientation" method) how do I detect the current iPad orientation? I have a text field I have to animate up (when keyboard appears) in the Landscape view, but not in the portrait view and want to know which orientation I'm in to see if the animation is necessary.
Orientation information isn't very consistent, and there are several approaches. If in a view controller, you can use the interfaceOrientation property. From other places you can call:
[[UIDevice currentDevice] orientation]
Alternatively, you can request to receive orientation change notifications:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
Some people also like to check the status bar orientation:
[UIApplication sharedApplication].statusBarOrientation
I think
[[UIDevice currentDevice] orientation];
is not really reliable. Sometimes it works, sometimes not... In my apps, I use
[[UIApplication sharedApplication]statusBarOrientation];
and it works great!
One of:
Check the interfaceOrientation property of the active view controller.
[UIApplication sharedApplication].statusBarOrientation.
[UIDevice currentDevice].orientation. (You may need to call -beginGeneratingDeviceOrientationNotifications.)
I found a trick to solve the FaceUp orientation issue!!!
Delay the orientation check till AFTER the app has started running, then set variables, view sizes, etc.!!!
//CODE
- (void)viewDidLoad {
[super viewDidLoad];
//DELAY
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(delayedCheck)
userInfo:nil
repeats:NO];
}
-(void)delayedCheck{
//DETERMINE ORIENTATION
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait ){
FACING = #"PU";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown ){
FACING = #"PD";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft ){
FACING = #"LL";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight ){
FACING = #"LR";
}
//DETERMINE ORIENTATION
//START
[self setStuff];
//START
}
-(void)setStuff{
if( FACING == #"PU" ){
//logic for Portrait
}
else
if( FACING == #"PD" ){
//logic for PortraitUpsideDown
}
else{
if( FACING == #"LL"){
//logic for LandscapeLeft
}
else
if( FACING == #"LR" ){
//logic for LandscapeRight
}
}
//CODE
You can addSubviews, position elements, etc. in the 'setStuff' function ... anything that would initially depend on the orientation!!!
:D
-Chris Allinson
You can achieve this by two ways:
1- By using the following method:
**Put the following line in the -(void)viewDidLoad Method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceRotated:) name:UIDeviceOrientationDidChangeNotification object:nil];
then put this method inside your class
-(void)deviceRotated:(NSNotification*)notification
{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
//Do your textField animation here
}
}
The above method will check the orientation when the device will be rotated
2- The second way is by inserting the following notification inside -(void)viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkRotation:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
then put the following method inside your class
-(void)checkRotation:(NSNotification*)notification
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
//Do your textField animation here
}
}
The above method will check the orientation of the status bar of the ipad or iPhone and according to it you make do your animation in the required orientation.
For determining landscape vs portrait, there is a built-in function:
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
BOOL inLandscape = UIDeviceOrientationIsLandscape(orientation);
[UIApplication sharedApplication].statusBarOrientation returns portrait when it's landscape, and landscape when it's portrait at launch, in iPad
I don't know why, but every time my app starts, the first 4 are right, but subsequently I get the opposite orientation. I use a static variable to count this, then have a BOOL to flip how I manually send this to subviews.
So while I'm not adding a new stand-alone answer, I'm saying use the above and keep this in mind. Note: I'm receiving the status bar orientation, as it's the only thing that gets called when the app starts and is "right enough" to help me move stuff.
The main problem with using this is the views being lazily loaded. Be sure to call the view property of your contained and subviews "Before" you set their positions in response to their orientation. Thank Apple for not crashing when we set variables that don't exist, forcing us to remember they break OO and force us to do it, too... gah, such an elegant system yet so broken! Seriously, I love Native, but it's just not good, encourages poor OO design. Not our fault, just reminding that your resize function might be working, but Apple's Way requires you load the view by use, not by creating and initializing it
In your view controller, get the read-only value of self.interfaceOrientation (the current orientation of the interface).
I've tried many of the above methods, but nothing seemed to work 100% for me.
My solution was to make an iVar called orientation of type UIInterfaceOrientation in the Root View Controller.
- (void)viewDidLoad {
[super viewDidLoad];
orientation = self.interfaceOrientation; // this is accurate in iOS 6 at this point but not iOS 5; iOS 5 always returns portrait on app launch through viewDidLoad and viewWillAppear no matter which technique you use.
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return YES;
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
orientation = toInterfaceOrientation;
}
Then, any place where you need to check the orientation you can do something like this:
if(UIInterfaceOrientationIsPortrait(orientation)){
// portrait
}else{
// landscape
}
There may still be a better way, but this seems to work 98% of the time (iOS5 notwithstanding) and isn't too hard. Note that iOS5 always launches iPad in portrait view, then sends a device the willRotateTo- and didRotateFromInterfaceOrientation: messages, so the value will still be inaccurate briefly.
[UIDevice currentDevice].orientation works great.
BUT!!!
... the trick is to add it to - (void)viewWillAppear:(BOOL)animated
exp:
(void)viewWillAppear:(BOOL)animated{
...
BOOL isLandscape = UIInterfaceOrientationIsLandscape(self.interfaceOrientation);
...
}
If you call it at - (void)viewDidLoad, it does not work reliable, especially if you use multiple threads (main UI thread, background thread to access massive external data, ...).
Comments:
1) Even if your app sets default orientation portrait, user can lock it at landscape. Thus setting the default is not really a solution to work around it.
2) There are other tasks like hiding the navigation bar, to be placed at viewWillAppear to make it work and at the same time prevent flickering. Same applies to other views like UITableView willDisplayCell -> use it to set cell.selected and cell.accessoryType.

Resources