Multiple UIWindows and which one controls the status bar orientation - ios

If you have an app with one UIWindow, my understanding is that the rootViewController of that UIWindow will be the UIViewController that receives the rotation/orientation methods like shouldAutoRotate, shouldAutoRotateToInterfaceOrientation, etc.
I'm writing an external library and there is an instance where I create another UIWindow object, set its rootViewController, and make it key and visible. It seems like the rootViewController of the original window is the one that still gets sent the rotation methods and not the new one.
I want to be able to control whether the application can rotate or not while the new window is visible, but it seems like the original window's rootViewController still has control over that. I've tried setting the original window's rootViewController to a rootViewController that prohibits rotation of the screen while my new window is visible and resetting the original window's rootViewController to its original rootViewController but that causes some problems of its own.
Does anyone know how to make a certain UIViewController the one in charge of app rotation?

How are you presenting the new viewController? According to the documentation,the only viewControllers that are asked about supportedInterfaceOrientations are the root view controller, or a view controller that fills the screen. So, on an iPhone, your new viewController should receive the supportedInterfaceOrientations call if it is filling the screen (e.g., presented modally).
shouldAutoRotateToInterfaceOrientation was deprecated as of iOS 6, so you should override supportedInterfaceOrientations instead.

This has worked for me...
In my Case, the destination view appears correct but the status bar and also the UIKeyboard keeps the landscape configuration, making a real mess.
Working around After thousands of recommendations about statusBarOrientation and references read... https://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html
"The setStatusBarOrientation:animated: method is not deprecated outright. It now works only if the supportedInterfaceOrientations method of the top-most full-screen view controller returns 0. This makes the caller responsible for ensuring that the status bar orientation is consistent."
statusBarOrientation only works if supportedInterfaceOrientations returns 0, so... that give us a guess.
If statusBarOrientation is not as expected, one zero return will do it (if always return 0, the view wont rotate, so:
// if deviceOrientation is A (so I expect statusbarOrientation A
// but statusbarOrientation is B
// return 0
// otherwise
// return user interface orientation for A
- (NSUInteger)supportedInterfaceOrientations {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
UIInterfaceOrientation statusBarOrientation =[UIApplication sharedApplication].statusBarOrientation;
if(deviceOrientation == UIDeviceOrientationPortrait || deviceOrientation == UIDeviceOrientationPortraitUpsideDown){
if(statusBarOrientation != UIInterfaceOrientationPortrait ||statusBarOrientation != UIInterfaceOrientationPortraitUpsideDown){
return 0;
}
}
// otherwise
return UIInterfaceOrientationMaskPortrait;
}
Now, in viewDidAppear (believe me, I use this call even when the keyboard notification is recived:
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;

Related

willAnimateRotationToInterfaceOrientation not called on ios6/7

I have an old app, that still lives on iTunes, written in iOS 5. I would like to update it to run on ios 6 and 7. Everything has been fine so far, and I have updated my code to use ARC. However when trying to maintain the same autorotation philosophy I keep hitting a brick wall. I have already checked relative topics within SO like:
Forcing landscape and autorotate in iOS 7,
Autorotate in iOS 6 has strange behaviour
and following a similar topic I have found this:
iOS 6 Autorotation Problems and Help
which lead me to do the following:
I have set the rootViewController within my AppDelegate like so:
self.preloadingViewController = [[PreloadingViewController alloc] initWithNibName:#"PreloadingViewController" bundle:nil];
self.window.rootViewController = self.preloadingViewController;
I have placed:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
within my AppDelegate. I have overriden shouldAutorotate and supportedInterfaceOrientations within the SuperViewController (parent in inheritance terms) of all of my app's UIViewControllers (including PreloadingViewController mentioned above):
- (BOOL)shouldAutorotate{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
and in every child UIViewController, I override
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
with code to layout ui elements in the desirable manner for portrait and landscape orientations.
Finally my app's plist file under
Supported interface orientations
contains:
Portrait (bottom home button), Landscape (left home button), Landscape
(right home button)
all the orientations I want to support.
Still, even though supportedInterfaceOrientations and shouldAutorotate are being called for every orientation change on my rootViewController, willAnimateRotationToInterfaceOrientation is never being called. I have even overriden shouldAutomaticallyForwardRotationMethods in my SuperViewController to return YES, but to no avail.
What am I doing wrong here? Any ideas? I have even considered that the old ios5 - style xibs cause the issue but I do not think this is the case.
Thanks in advance.
In iOS 6 the way willAnimateRotationToInterfaceOrientation is called changed.
Use:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Something
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// Something
}
in your rootController.
EDIT:
New main.m
#import <UIKit/UIKit.h>
#import "yourAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([yourAppDelegate class]));
}
}
Since my rootViewController was the only one getting its shouldAutorotate and supportedInterfaceOrientations methods called, I decided to register every other view controller to get notified of any UIDeviceOrientationDidChangeNotification.
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
- (void)orientationChanged:(NSNotification *)notification
{
NSLog(#"Orientation changed!");
[self layoutForOrientation:[[UIDevice currentDevice] orientation]];
}
Within my layoutForOrientation: method I handled the uiview's controls orientation.
However, although I did receive UIDeviceOrientationDidChangeNotification notifications normally, my view orientation would not actually change to match the current orientation, i.e. if the new orientation was say, UIInterfaceOrientationLandscapeRight, the view orientation would remain in UIInterfaceOrientationPortrait and its children views would get rotated by 90 degrees. This did certainly not look right. After pulling a lot of hair out, I decided to abandon this route.
Instead, I have set my rootViewController to be a UINavigationController and have it push successive UIViewControllers on top of it. Since I did not want a navigation bar visible in my app I have set the navigation controller's navigationBarHidden property set to YES. This way the method willAnimateRotationToInterfaceOrientation is getting called on every UIViewController that is currently at the top of the navigationCotroller's stack of controllers, and its corresponding view and view controls rotate correctly for the desired orientations.
This works because most of my view controllers supported interface orientations match the mask: UIInterfaceOrientationMaskAllButUpsideDown, the default behaviour for iPhone. Should I needed to support different orientations, I would have to subclass the UINavigationController and override supportedInterfaceOrientations and shouldAutorotate to enable the desired orientations support for the navigation stack's top view controller, as per Apple's example project: AlternateViews.
Implement this method, return YES for what you need.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation

Want to lock orientation for app

I want to lock Orientation for entire application if server sends 'Lock' message. When server send 'Unlock' orientation , i want the app to do auto rotate. I stored the value 'Lock' in NSUSerDefaults. In rootViewcontroller , it is working fine like if Lock is true, shouldAutoRotate method returns NO, and view does not rotate. But in other view controller, even lock is true, shouldAutoRotate is returning No, but View is still rotating.
I have created a category of UINavigationController for autorotation so that shouldAutorotate method is called from every class.
- (BOOL)shouldAutorotate
{
NSString *AUTO_ROT =[[NSUserDefaults standardUserDefaults]valueForKey:#"AUTOROT"];
if([AUTO_ROT caseInsensitiveCompare:#"true"] == NSOrderedSame)
{
return YES;
}
else{
return NO;
}
}
Please help me in this.
Are you sure the rotating view controller is also a child view controller of a UINavigationController (i.e. did you push it on the UINagivationController)?
If you presented it using presentViewController:animated:completion: for example the view controller itself would also need to implement shouldAutorotate.
On a side note, be cautious of overriding via category. A much better way would be to subclass UINavigationController and override the method in the subclass.

Different supported interface orientations in UINavigationController stack

I have a UINavigationController as my UIWindow's rootViewController and a UIViewController (ControllerA) that supports only Portrait orientation is added to the UINavigationController as its rootViewController.
At some point later, I replace UINavigationController's rootViewController with a new UIViewController (ControllerB). ControllerB supports both Portrait and Landscape. I want my initial screen (ControllerA) to only work in Portrait while the rest of the app can supports both Portrait and Landscape.
[self.navigationController setViewControllers:#[viewControllerB] animated:NO];
| UINavigationController launches:
| ----ControllerA (rootViewController): Portrait only
| ----ControllerB (rootViewController): Portrait and Landscape supported
If I start my app in Landscape which is not handled by ControllerA and then move to ControllerB, my content (+status bar) is still in Portrait. If I manually rotate the device at this point, I have the correct content layout.
How can I make ControllerB render itself in the orientation of the device?
Here's what I'm seeing from both ControllerA and ControllerB viewWillAppear method:
navigationController orientation: UIInterfaceOrientationPortrait
UIViewController interfaceOrientation: UIInterfaceOrientationPortrait
<!-----------!!two values are different!!------------------>
[[UIDevice currentDevice] orientation]: UIDeviceOrientationLandscapeLeft
[[UIApplication sharedApplication] statusBarOrientation]: UIInterfaceOrientationPortrait
Phone is physically held in Landscape at this point in time.
Here is the easiest solution (iOS7 only) but it can be expanded to work down to iOS5.
Just write our own UINavigationController (or write a category) and implement supportedInterfaceOrientations.
#implementation MyNavigationController
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
#end
When you want to know the interface orientation always use UIViewController.interfaceOrientation. The statusbar orientation might be wrong (in case of multiple windows) and the device orientation is totally wrong since the device can be Face Up (laying flat on a table) and you cannot infer the interface orientation from that.

UIModalPresentationCurrentContext and willRotateToInterfaceOrientation

I have a modal view controller (let's call it the popup view controller) presented over my main view controller. At some point, I need so see the main view controller behind the popup. Therefore, I set the modalPresentationStyle property to UIModalPresentationCurrentContext before presenting the popup view controller.
So what happens is that when the device orientation changes while the popup view controller is modally presented, the willRotateToInterfaceOrientation: and didRotateFromInterfaceOrientation: methods are not called. supportedInterfaceOrientations is called amd returns the good value. The rotation is enabled, the supported orientations are correctly set. The popup conIt actually works if I change 'modalPresentationStyle' to the default value, everything works fine, except that obviously I do not see the main view controller behind.
I should add that the main view controller only supports portait, while the popup above it supports all orientations.
On an iOS 5.1 device, the willRotate and didRotate methods are correctly called. It is only on the iOS 6 device that they are not.
Did anybody encountered a similar issue or already needed to display a transparent multi-orientation view controller modally above a single orientation view controller?
Do this...
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation {
UIDevice* thisDevice = [UIDevice currentDevice];
if (thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
return true;
}
else
{
return interfaceOrientation == UIDeviceOrientationLandscapeLeft;
}
}

willRotateToInterfaceOrientation not being called from presented viewcontroller

I have uiviewcontroller on ipad with this configuration:
shouldAutorotate (true)
supportedInterfaceOrientations (UIInterfaceOrientationMaskAll)
and inside willRotateToInterfaceOrientation i perform some trick to adjust my interface.
From a child of this controller I show a QuickLookController with this -poor- code.
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:previewController animated:YES completion:nil];
But if I rotate my ipad the method willRotateToInterfaceOrientation not being called, So I cannot do the trick to adjust the interface.
Someone can explain me or given me some advices? thanks
Reason :
There may be many possibilities to this problem.
1) If your view's viewController is a subView of some other rootViewController which is not a navigationController, then there might be chances that rotation call is not propagating to the subView's controller.
2) Somewhere I read that if Super methods are not called properly where it is needed then it might be the cause of rotation problem, which means that all ViewControllers in view stack which are related to the autorotation must call the super methods in method implementations (i.e. calling [super viewDidLoad] from the ViewController's viewDidLoad).
You can use below trick to handle orientation changes.
Register a notifier in viewWillAppear.
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];}
The orientation change will notify the below function.
- (void)orientationChanged:(NSNotification *)notification{
[self handleOrientation:[[UIApplication sharedApplication] statusBarOrientation]];}
which will call the below method where you can handle the orientation changes.
- (void) handleOrientation:(UIInterfaceOrientation) orientation {
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
//handle the portrait view
}
else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
//handle the landscape view
}
}
I found a paragraph on apple documentation:
If your view controller’s contents are not onscreen when a rotation occurs, then it does not see the list of rotation messages. For example, consider the following sequence of events:
Your view controller presents another view controller’s contents full screen.
The user rotates the device so that the user interface orientation changes.
Your app dismisses the presented view controller.
In this example, the presenting view controller was not visible when the rotation occurred, so it does not receive any rotation events. Instead, when it reappears, its views are simply resized and positioned using the normal view layout process. If your layout code needs to know the current orientation of the device, it can read the app object’s statusBarOrientation property to determine the current orientation.
that say exactly what is my problem, so i add some logic inside
-(void)viewWillAppear:(BOOL)animated
to adjust my layout if something is'n in the right place.
Ensure your view controller is added as a child of the root view controller, then the messages are passed down.
[rootViewController addChildViewController:viewController];

Resources