When changing UIWindow subviews, orientation is not set - iphone

If a view is added to the window, orientation is set as portrait even if the device is in landscape.
If the view is added in the app delegate, application:didFinishLaunchingWithOptions: method, then it works correctly. But if the view is added later it does not.
As an example, I have a routine to switch views. Simplest form is:
- (void)switchToNewViewController:(UIViewController *)newViewController {
if ([[window subviews]count]!=0) {
[[[window subviews]objectAtIndex:0] removeFromSuperview];
}
[window addSubview:newViewController.view];
}
IF this is called from within didFinishLaunching, orientation is correct. If it is not, orientation is portrait.
Simplest case is within didFinishLaunching I have the following two lines
// The following line works
[self switchToNewViewController:fullScreenViewController];
// The following line which delays the method call until later results
// in incorrect orientation
[self performSelector:#selector(switchToNewViewController:) withObject:fullScreenViewController afterDelay:0.1];
Is there a way to make the view have the proper orientation?

Make sure your shouldAutorotateToInterfaceOrientation in the view controllers has the right logic
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES; //this supports all orientations
}
If you are linking stuff in InterfaceBuilder also make sure both the view and the viewcontroller are configured to the initial orientation you like. (There is a little arrow in the top right corner to rotate views and view controllers)
If you still have problems, are you using a UINavigationController or similar? UINavigationController needs to be subclassed and shouldAutorotateToInterfaceOrientation implemented if you want to support something other than portait.

Related

Handling autorotation for one view controller in iOS7

I've read many answers on SO but I can't seem to get autorotation working on iOS7.
I only need one view controller to rotate, so I don't want to set rotation settings in my Info.plist.
As I understand Apple's documentation, a single view controller can override global rotations settings (from Info.plist) by simply overriding two methods. Info.plist is set to only allow Portrait, and my view controller implements the following methods:
- (NSUInteger)supportedInterfaceOrientations
{
NSLog(#"%s", __PRETTY_FUNCTION__);
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (BOOL)shouldAutorotate
{
NSLog(#"%s", __PRETTY_FUNCTION__);
return true;
}
I'm seeing those NSLog statements upon rotation but nothing rotates.
If I do configure Info.plist with the proper rotation settings, my view will rotate, but not if I try and rely on my view controller.
Not sure if it matters, but the view I'm trying to rotate is from a .xib using auto layout.
Also, my ViewController is being presented modally and is contained in a navigation controller. I've tried just presenting the view controller by itself and that doesn't work. I've also tried adding a category to UINavigationController to get it's autorotation directions from it's topViewController.
In my case, I had a new iOS7 app with about 30 view controllers created already. I needed auto rotation on just a single modal view controller. I didn't want to have to update the preexisting view controllers.
I selected the orientations I wanted in the plist:
Then I added a category to my app delegate on UIViewController:
#implementation UIViewController (rotate)
-(BOOL)shouldAutorotate {
return NO;
}
#end
Then in the single modal view controller I WANTED to rotate I added this method:
-(BOOL)shouldAutorotate {
return YES;
}
I also discovered, that if my view controller wasn't a modal VC I would need to add category methods on UINavigationController instead, for all VCs that were subsequent to the root view controller, as part of the navigation stack of view controllers - similar to this: https://stackoverflow.com/a/20283331/396429
Simple but it work very fine. IOS 7.1 and 8
AppDelegate.h
#property () BOOL restrictRotation;
AppDelegate.m
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
return UIInterfaceOrientationMaskPortrait;
else
return UIInterfaceOrientationMaskAll;
}
ViewController
-(void) restrictRotation:(BOOL) restriction
{
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = restriction;
}
viewDidLoad
[self restrictRotation:YES]; or NO
You need to set the plist value to all possible values, then limit them as you see fit (in the Navigation Controllers and TabBar Controllers. From the UIViewController class description:
In iOS 6 and later, your app supports the interface orientations
defined in your app’s Info.plist file. A view controller can override
the supportedInterfaceOrientations method to limit the list of
supported orientations. Typically, the system calls this method only
on the root view controller of the window or a view controller
presented to fill the entire screen; child view controllers use the
portion of the window provided for them by their parent view
controller and no longer participate directly in decisions about what
rotations are supported. The intersection of the app’s orientation
mask and the view controller’s orientation mask is used to determine
which orientations a view controller can be rotated into.
I've faced such problem - had only one landscape view in my app.
I've used below code to to handle that.
#import "objc/message.h"
-(void)viewWillAppear:(BOOL)animated{
objc_msgSend([UIDevice currentDevice], #selector(setOrientation:), UIInterfaceOrientationLandscapeLeft);
}
I know this is old but I ended up in a more unique situation where we have 50+ ViewController all over the app that I refused to go through and modify and support the same orientation in all of them but one or 2. Which brings me to my answer. I created a UIViewController category that overrides - (BOOL)shouldAutorotate to always return NO or YES depending on device type etc. (this can be done with supported interface orientations too). Then on the ViewControllers I wanted to support more then just portrait, I swizzled shouldAutorotate to return YES. Then forced the orientation change when the view is dismissed on the parent ViewControllers viewWillAppear method using:
[[UIDevice currentDevice] setValue:#(UIInterfaceOrientationPortrait) forKey:#"orientation"].
When all was said and done, I accomplished everything I wanted on a few ViewControllers with < 30 lines of code using a macro for swizzling. Had I done it by replacing shouldAutorotate and supportedInterfaceOrientations on all of the VC's in the application I would have ~250 extra lines of code. and a lot of grunt work adding it in the first place.

Autorotation in iOS6

I am updating my app for iOS 6 and having issues with the changes to autorotation. My app has a bunch of view controllers and all of them should only support the portrait layout except 1 which should support all 3 orientations except upside down.
If I add the application:supportedInterfaceOrientationsForWindow: method to the app delegate do I have to add conditions there to check if im displaying the one VC I want to be able to rotate?
The documentation states that if I implement supportedInterfaceOrientations on a VC it should override the app delegate method but this doesn't appear to be the case. I have an log statement in the method on the child VC and it is called once when the VC loads but its not called when I rotate the device, but the method in the app delegate is.
If I completely remove the method from the app delegate the orientation of my VC's seems to be completely dependent on my apps supported interface orientation settings. This of course seems to be due to the method supportedInterfaceOrientations being called once on creation of the VC but never when the device is rotated.
Does anyone have any ideas or suggestions? It would be much appreciated.
Replace
[window addSubview:viewController.view];
with
window.rootViewController = viewController;
You also need to override - (BOOL) shouldAutorotate and return "YES". This makes it so you declare what orientations your VC supports with "supportedInterfaceOrientations" and then on rotation it should call "shouldAutorotate". If you have any navigation controller or tabbar you may need to subclass those to do the same thing within them. I had this issue recently myself.
try this...
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
// here to implement landscope code
}
else
{
// here to implement setframePortrait
}
}

iphone screen orientation

Hi I am trying to provide screen orientation in my app and I have read many docs online but when I try that It just is a total disaster (I tried emulator and iphone). On one partwhen the screen is rotated to landscape half of the views are not visible any more. Their position is on the left side and right side is empty, plus if I open a next view controller and then rotate again I can see through the second view parts of the first view it just is terrible I don't know how to fix it (Excluding blocking the possibility to lock orientation in portrait).
I know in android you can provide different xml layouts for portrait and landscape. Is there a way to fo that in iphone? If so how?
each of my views are composed of a controller and .xib
There is a "main" controller and the other controllers are called like that:
ayuda=[[AyudaView alloc]
initWithNibName:#"AyudaView"
bundle:nil];
[self.view addSubview:ayuda.view];
also I have already added these metods to the maincontroller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(UIDeviceOrientationIsValidInterfaceOrientation(orientation)) {
[self handleInterfaceRotationForOrientation:orientation];
}
}
#pragma mark -
#pragma mark Screen Orientation Handling
- (void)handleInterfaceRotationForOrientation:(UIInterfaceOrientation)interfaceOrientation {
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
[self handleInterfaceRotationForOrientation:interfaceOrientation];
return YES;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self handleInterfaceRotationForOrientation:toInterfaceOrientation];
}
as indicated here
and have tried to play with the autosizing param but it only gets worse
Thank you very much
EDIT: ok I have been reading some more:
http://www.geckogeek.fr/iphone-forcer-le-mode-landscape-ou-portrait-en-cours-dexecution.html
http://www.cocoaosx.com/2011/11/10/rotatingviewcontroller-display-a-different-uiviewcontroller-depending-on-the-rotation-of-the-device/
and basically what I have found is that you have to create 2 controllers but this solution is problematic for me because of the nib files. I mean I would also have to create 2 nibs so double the amount of code (or use the library above). What if I have values in my textfields , do I just have to send them around?
So, apart from the comments suggesting you don't have any idea about what's going on in your code (which may or may not be true -- who am I to judge?), I see a few problems.
The first issue is that I don't think you understand MVC and how iOS handles the view hierarchy. You're correct in having each of your views controlled by a controller and designed by a .xib (there are cases where a view doesn't need its own controller, but for simplicity, we'll assume yours do). However, you are incorrect in the way you are presenting those views on screen. A view's controller is responsible for presenting or removing a view from the screen. The view only knows what it displays, such as labels or buttons. The controller is responsible for telling the view what to display on those labels and buttons. Instead of adding the new VC's view as a subview of your main view, push it onto a navigation controller's stack or present the view modally. If you're in your main VC, you can say
ayuda = [[AyudaView alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:ayuda animated:YES];
I'm thinking that getting the view hierarchy correct might just be enough to solve your landscape/portrait swapping chaos. But while we're on that topic...why are you creating this handleInterfaceRotation method? View Controllers already have a method for this called
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
Use this method to handle rotation.

UIViewController orientations

I have a UIViewController instance(viewController1) that I want to lock in landscape mode which I am able to do. On tapping on a button I push another UIViewController(viewController2) which supports both orientations. Now, if user changes the orientation of viewController2 to portrait and goes back to viewController1, viewController1 also changes it's orientation to portrait. How can I avoid that?
Thanks in advance.
Add these methods to the view controllers
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Thats for the first
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES);
}
Thats the second
-(BOOL)shouldAutorotateToInterfaceOrientation:UIInterfaceOrientation)toInterfaceOrientation{
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
return YES;
}
return NO;
}
If those 2 controllers are both implementation of UIViewController both differente classes each other! you can just implement the method shouldAutorotateToInterfaceOrientation, in the first! this should work even when u go back!
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
return YES;
}
return NO;}
You say push so I assume both ViewControllers are in a NavigationController. If so I'll have to disappoint you, what you want isn't possible. Your rotation callbacks are working correctly, they respond to a rotation, you can't force it. What's happening is the correct behavior.
Best solution is to prevent the user from going back when you're in the orientation the previous ViewController doesn't support, hide the back button for example.
A while back I've made my own NavigationController (doesn't inherit from the UIViewController but it can do exactly the same) and I've tried to implement what you're trying to do. Before pushing or popping, if the view of the ViewController that was about to be shown didn't support the current orientation, I transformed the view of that ViewController by 90° and force the orientation of the status bar to the new ViewController's supported orientation.
As soon as the push or pop was complete I'd do a small trick to force the rotation of the device. If you remove the view of the rootViewController from the window and re-add it, the responder chain will be forced to go through all rotation callbacks. When that happened I checked if a ViewController's view was transformed and reset that transformation.
It did work, mostly. But it was messy code and it goes against Apple's current policy that the rootViewController should be responsible of handling the orientation. Also in iOS6 forcing the status bar orientation is guaranteed to work. So I'd really advise against doing this, I've removed this from my own NavigationController too.

Auto-rotation support for view added via presentModalViewController?

It seems that no matter what orientation I am supporting on my views in my app, when I display a view with presentModalViewController, the app snaps into portrait view. How can I support different orientations with a modal view controller?
The controller that you present using presentModalViewController:animated: should support any orientation, typically using this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
With this method, when you present your controller in landscape orientation, it will correctly appear in landscape orientation, too.
I had same problem. I set TRUE AutorotateInterfaceOrientation but at the begin my modalview was always presented Portrait even if it was Landscape. Only if i changed device orientation (in emulator) it's behavior became correct. But at start no!
I don't know why, but all depends from where you present view.
If you present, for example, from viewDidLoad method, then problem occurs. For solving problem, i call presentModalView from a method that's called in viewDidLoad (apparently, it seems same thing). So, i did something like:
- (void) viewDidLoad
{
[super viewDidLoad];
// add this line
[self performSelector:#selector(presentModal) withObject:nil afterDelay:0];
}
-(void) presentModal{
// here present your view
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
This works for me
I just solved this problem for myself by adding code in viewDidAppear. I think the reason it didn't show up in the proper orientation for me is because I had the show modal code in viewDidLoad and at that point, I think the view had just loaded but the orientation was not yet set. So, adding it in viewDidAppear worked for me. You will probably also have luck in viewWillAppear (because by that time, it has to know what the orientation, bounds, sizes, etc are).