Please note that this question is from 2008 and now is of only historic interest.
What's the best way to create an iPhone application that runs in landscape mode from the start, regardless of the position of the device?
Both programmatically and using the Interface Builder.
Historic answer only. Spectacularly out of date.
Please note that this answer is now hugely out of date/
This answer is only a historical curiosity.
Exciting news! As discovered by Andrew below, this problem has been fixed by Apple in 4.0+.
It would appear it is NO longer necessary to force the size of the view on every view, and the specific serious problem of landscape "only working the first time" has been resolved.
As of April 2011, it is not possible to test or even build anything below 4.0, so the question is purely a historic curiosity. It's incredible how much trouble it caused developers for so long!
Here is the original discussion and solution. This is utterly irrelevant now, as these systems are not even operable.
It is EXTREMELY DIFFICULT to make this work fully -- there are at least three problems/bugs at play.
try this .. interface builder landscape design
Note in particular that where it says "and you need to use shouldAutorotateToInterfaceOrientation properly everywhere" it means everywhere, all your fullscreen views.
Hope it helps in this nightmare!
An important reminder of the ADDITIONAL well-known problem at hand here: if you are trying to swap between MORE THAN ONE view (all landscape), IT SIMPLY DOES NOT WORK. It is essential to remember this or you will waste days on the problem. It is literally NOT POSSIBLE. It is the biggest open, known, bug on the iOS platform. There is literally no way to make the hardware make the second view you load, be landscape. The annoying but simple workaround, and what you must do, is have a trivial master UIViewController that does nothing but sit there and let you swap between your views.
In other words, in iOS because of a major know bug:
[window addSubview:happyThing.view];
[window makeKeyAndVisible];
You can do that only once. Later, if you try to remove happyThing.view, and instead put in there newThing.view, IT DOES NOT WORK - AND THAT'S THAT. The machine will never rotate the view to landscape. There is no trick fix, even Apple cannot make it work. The workaround you must adopt is having an overall UIViewController that simply sits there and just holds your various views (happyThing, newThing, etc). Hope it helps!
From the Apple Dev Site:
To start your application in landscape
mode so that the status bar is in the
appropriate position immediately, edit
your Info.plist file to add the
UIInterfaceOrientation key with the
appropriate value
(UIInterfaceOrientationLandscapeRight
or
UIInterfaceOrientationLandscapeLeft),
as shown in Listing 2.
Listing 2: Starting your application
in landscape mode
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
Summary and integration from all the posts, after testing it myself; check the update for 4.x, 5.x below.
As of 3.2 you cannot change the orientation of a running application from code.
But you can start an application with a fixed orientation, although doing so this is not straightforward.
Try with this recipe:
set your orientation to UISupportedInterfaceOrientations in the Info.plist file
in your window define a 480x320 "base view controller". Every other view will be added as a subview to its view.
in all view controllers set up the shouldAutorotateToInterfaceOrientation: method (to return the same value you defined in the plist, of course)
in all view controllers set a background view with
self.view.frame = CGRectMake(0, 0, 480, 320)
in the viewDidLoad method.
Update (iOS 4.x, 5.x): the Apple iOS App Programming Guide has a "Launching in Landscape Mode" paragraph in the "Advanced App Tricks" chapter.
References:
interface builder landscape design
interface builder landscape design-1
First I set in info.plist
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
then I put this code in applicationDidFinishLaunching:
CGAffineTransform rotate = CGAffineTransformMakeRotation(1.57079633);
[window setTransform:rotate];
CGRect contentRect = CGRectMake(0, 0, 480, 320);
window.bounds = contentRect;
[window setCenter:CGPointMake(160.0f, 240.0f)];
This way I can work on the view in Interface Builder in landscape mode.
sasb's and michaelpryor's answer appears to be correct, but if it's not working for you, try this alternative:
- (void)applicationDidFinishLaunchingUIApplication *)application {
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
}
Or this one:
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
Or this one:
[application setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
You may also have to call window makeKeyAndVisible; first.
A few links: Developing in landscape mode, iPhone SDK: How to force Landscape mode only?
#Robert: please refer to The iPhone SDK, NDA, and Stack Overflow.
I'm surprised no one has come up with this answer yet:
In all my tests when a dismissing a modal view controller the parent view controller's preferred orientation set in shouldAutorotateToInterfaceOrientation is honored even when part of a UINavigationController. So the solution to this is simple:
Create a dummy UIViewController with a UIImageView for a background. Set the image to the default.png image your app uses on startup.
When viewWillAppear gets called in your root view controller, just present the dummy view controller without animation.
when viewDidAppear gets called in your dummy view controller, dismiss the view controller with a nice cross dissolve animation.
Not only does this work, but it looks good! BTW, just for clarification i do the root view controller's viewWillAppear like this:
- (void)viewWillAppear:(BOOL)animated
{
if ( dummy != nil ) {
[dummy setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:dummy animated:NO];
[dummy release];
dummy = nil;
}
...
}
The latest iPhone OS Programming Guide has a full section on this, with sample code. I am sure this is a recent addition, so maybe you missed it. It explains all the conditions you have to comply with; basically...
set the Info.plist properties (this changes the position of the status bar, but not the view)
rotate your view manually around its center, on either your UIViewController viewDidLoad: method or your applicationDidFinishLaunching: method or implement auto rotation ("Autoresizing behaviors", page 124)
Look for "Launching in Landscape Mode", page 102.
See this answer: Landscape Mode ONLY for iPhone or iPad
add orientation to plist
shouldAutorotateToInterfaceOrientation = YES in all files
Although if you're using mixed modes, you might be better off with
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
Related
when first entry, the result like this
App into the background and back, the orientation is normal
this is the code for the ViewController:
DDYLoveAutoHallViewController *autoHallViewController = [[DDYLoveAutoHallViewController alloc] initWithEvent:room.currentEventID type:AHRoom_System];
[self presentViewController:autoHallViewController animated:YES completion:nil];
i don't know why, the project is a litter old, it support iOS5 before.
Without understanding on how do you manage layouts it is difficult to give the exact reason, caused you problem, but, i can suggest you couple of little advices:
try to re-check orientation in - (void)viewDidLayoutSubviews, and you can re-position things from there. (probably it will work and solve your problem, but it is not very clean solution, because may be called a few times at time, knowing this you can make it cleaner)
play generally with the methods of layout-lifecycle, to detect where are things is going wrong. Dive deeper in modern way to layout on iOS. (better solution)
Without more details regarding your specific problem, and how you are detecting and adjusting the orientation, it is hard to understand the exact problem, but here are some observations I have made, working with iOS 5.x based (and earlier), apps, that might point you in the correct direction:
[1] Earlier versions of iOS set some defaults, before actually interrogating the hardware to determine orientation and app frame size information.
a) Initially orientation is set to PORTRAIT - along with portrait orientation frame dimensions - during initialization. (This is WRONG if you device is in landscape orientation - so DON’T USE this initial info). (left over from earlier “iPhone only days” - I guess)
b) This continues to be incorrect at the “ViewDidLoad” and “applicationDidBecomeActive” timeframes (at least for my app - possibly - depends on loading time etc…)
c) The correct orientation is yielded later, via the “didChangeStatusBarOrientation method invocation. You can use this information with Window.frame.size information, to display the correct image with the correct size.
This is effectively the trigger to indicate the orientation request will now be correct.
One stategy might be to : Don’t try to display anything until the “didChangeStatusBarOrientation” message has been received.
[2] Non-Code Solution: (using Settings : Supported interface Orientations)
a) If the app is designed to always work in the Landscape orientation, make sure this is reflected in the app settings/info.plist. I would suggest only allowing 1 “supported interface Orientations”, that being Landscape, and all views would reflect the landscape size and orientation.(if your code is not specifically setting or doing anything with orientation, then this might be a solution by setting everything to "landscape only". Otherwise this option probably won't help.)
[3] Last Solution:
a) If you can change the base iOS version higher, the iOS6 and greater versions initialize the orientation and frame sizes earlier in the initialization cycle, so the problem may just disappear due to this.
Here is some of the code used to discover this, along with output below (using an original ipad as a sample, in this case…running iOS 5.1.1 - started in landscape and orientation was unchanged)
-(void) OrientationAndScreenSizeHELPER : (NSString *)fromObject
{
//from:
// NSLog(#"%s:%d someObject=%#", __func__, __LINE__, someObject);
CGRect appFrame = [[UIScreen mainScreen ]applicationFrame];//using frame so status bar is not part of calculations.
appFrame = [[self.viewController view]frame];//using frame so status bar is not part of calculations.
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
NSLog(#"orientation[%ld] at time[%#] width[%ld] height[%ld]",
(long)orientation,
fromObject,
(long int)appFrame.size.width,
(long int)appFrame.size.height);
}
**Output:**
orientation[1] at time[ViewDidLoad Orientation] width[768] height[1024]
orientation[1] at time[applicationDidBecomeActive] width[768] height[1024]
orientation[3] at time[didChangeStatusBarOrientation] width[1024] height[768]
I'm using the VFR PDF viewer library in my app, where I present it thus:
ReaderDocument *document = [ReaderDocument withDocumentFilePath:pdfFile password:nil];
ReaderViewController *vc = [[ReaderViewController alloc] initWithReaderDocument:document];
[self.navigationController pushViewController:vc animated:YES];
If I run on iOS7, everything works fine.
If I run my app on iOS8, the willRotateToInterfaceOrientation method in ReaderViewController never gets called, so when the device is rotated the document doesn't get reformatted correctly.
However, if I run the demo app that comes with the library on iOS8, the willRotateToInterfaceOrientation in ReaderViewController does get called, which leads me to believe the library is ok, and I'm doing something wrong (or neglecting to do something) in my app.
I'm rather puzzled at this behaviour. Why doesn't willRotateToInterfaceOrientation get called in my app on iOS8, but it does under the other variations? How can I try to track this down?
I finally managed to resolve my problem; here is what the issue was in case anyone else has the same problem.
My ReaderViewController was being presented from a class that had implemented viewWillTransitionToSize:withTransitionCoordinator:, so the deprecated methods on child view controllers weren't being called.
Also in that implementation it wasn't calling
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
and so the viewWillTransitionToSize:withTransitionCoordinator: method of all child view controllers wasn't being called either.
The fix was to add a call to
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
into the parent VC's viewWillTransitionToSize:withTransitionCoordinator: method, subclass ReaderViewController, then add a viewWillTransitionToSize:withTransitionCoordinator: to my subclass to call the appropriate methods in ReaderViewController.
Simply because willRotateToInterfaceOrientation is no more called in iOS8, it is deprecated.
If the new rotation methods are not implemented, and a project is
compiled with the iOS 8 SDK, the view controllers -will not receive
calls- to the deprecated rotation methods.
A similar question to yours can be found here
Citation of #mluisbrown :
The rotation methods are deprecated in the iOS 8 SDK. This will have
no effect at all on apps built with the iOS 7 SDK, even running in iOS
8 and probably several future versions of iOS.
I struggled a bit with some kind of a similar problem to yours. I tried following Apple recommendation to use viewWillTransitionToSize, which in my case did not solve my problem because this only gets triggered on changes from regular to compact for example an not on rotation.
viewWillTransitionToSize:withTransitionCoordinator: to make
interface-based adjustments.
Which is detailed in apple documentation
Also a video of WWDC 2014 explains this but I can't remember which video it was. Perhaps the one on What's new in Cocoa touch or the one on View Controller Advancements in iOS 8.
EDIT
Note that in my case viewWillTransitionToSize, as explained, was not called because I wasn't changing from regular to compact so there was no size transition, strictly speaking for Apple.
The only solution I fount was to handle this manually in the viewDidLayoutSubviews of the corresponding view controller.
In my case I wanted to keep track of the top cell displayed in a tableview with autolayout. As it wasn't tracked automatically by the system, I had to check that manually and scroll manually to the adequate cell on rotation. That's why I did it that way. It's quite "heavy" but works in my case.
I would also be interested if anyone has an easier solution.
I am experiencing an odd problem with legacy code running in iOS8. The code was originally written pre-iOS6, pre-storyboards (using nibs) and functioned without issue on iOS7. However, strange behavior began to occur when running on iOS8.
The app is set to run only in landscape (left or right). Therefore it should support autoRotation from landscape-left to landscape-right. The first problem was that the initial view controller was loaded in portrait and one side was cut off. This issue was addressed with the following code in the AppDelegate:
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]];
Changing the order of these calls allowed the view controller to load in its proper orientation and did not affect apps running in iOS7. However, now when the device is rotated 180 degrees I get the following results. See images...
Here is the app at initial load:
And after a 180 degree rotation, I get this offset effect:
Any ideas on how to address this issue? Again, everything is fine in iOS7 or previous. THanks!
I had a problem just like yours when iOS 8 first appeared, and it had nothing to do with the opening window-creation incantation! It has to do with your Info.plist. It is crucial that the first orientation listed in the Info.plist be an orientation that the root view controller actually permits.
So, let's say your root view controller permits only landscape. Then you will get the problem you describe if your Info.plist looks like this:
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
See the bug? Portrait comes first. No. If the root view controller permits only landscape, then one of the landscape orientations must come first.
[You can actually see me experiencing the issue, then discovering and reporting the solution, here: https://stackoverflow.com/a/24467576/341994]
Also, I have to wonder whether you might be doing anything else that upsets the rotation system. Note that the nature of rotation itself, including the coordinate system, what rotates, when it rotates, etc., has completely changed in iOS 8; this is probably the biggest single change in iOS 8. So if you have old code that makes under-the-hood assumptions about what rotation is (e.g. that it involves a transform), that code will now break. You do not show any of that in your question so I can't provide specifics about what you might be doing.
Finally, just for the record, the correct minimal opening incantation is (this is Swift code but I'm sure you can translate):
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = UIViewController() // or whatever it is
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
You should use some autolayout constraints on the view controller so that it sticks to the left / upper and bottom of the parent container.
Here are a few ideas:
1) Initialize your window with a frame, don't set it afterwards
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
2) Make sure the autoresizingMask of your rootViewController's view is FlexibleWidth+FlexibleHeight and you don't have flexible margins anywhere in there.
3) Make sure your App's target explicitly has only the orientations you want in the General->Deployment Info section. It sounds like you have it only going into the orientations you want, but just check that to make sure!
I wasted a whole day with a similar problem while migrating our App from iOS 7.x to iOS 8.4. The views had mystic offset an wrong size after device rotation.
Our App uses a UITabBarController as RootViewController and the number of Tabs can change dynamically. On the iPad all views support all orientations. On the iPhone, only one view can be rotated, all other are firmly set to portrait. Because the number of views can change dynamically, I have a property that
stores the possible orientations for each view. I return the content of the property every time UIViewController.supportedInterfaceOrientations() was called from iOS.
While debugging the problem I noticed that the function UIViewController.supportedInterfaceOrientations() was called immediately after AppDelegate called the function UIWindow.MakeKeyAndVisible(). At this time, my code had no chance to initialize the property, so UIViewController.suppoertedInterfaceOrientations() returned 0 to iOS. After i changed the code so that my property is initialized before UIWindow.MakeKeyAndVisible() was called, the mystic
size and offset problem was gone!!!
My conclusion: Don't mess the iOS with a wrong or missing UIViewController.supportedInterfaceOrientations() function ;-)
I was given a project that was started by someone else who no longer works here.
I have a UITabBarController which holds some UIViewControllers.
If the application is running on iOS 6, everything runs fine, However as soon as I run it on iOS 5, all UIViews are rotated 90 degrees and given an origin value of something around -100 to -300
I have been able to loop through all view controllers of the tabBar and set
myView.transform = CGAffineTransformMakeRotation(0.0);
myView setFrame:CGRectMake(0,0,1024,748);
The initial view controllers on UITabBarController appear correctly, However, if I ever try to launch a modal view controller, everything is stuffed again. including the modal.
I am running out of ideas on how I could fix this once and for all. I couldn't find anything in the code that rotates the views.
What I could deduce is
on iOS 6, the first subview of the main view holding the UITabBarController is UILayoutContainerView
but on iOS 5 the first subview is of class UIView
If this is an issue with UILayoutContainerView not being supported in iOS5, how can I make the application backwards compatible now?
Note: we only support Landscape (Right/Left) and only on iPad.
Also, I have noticed that if the user rotates the application before initialiazing the UITabBarController and its sub controllers. everything works fine. Even if you re-run the application and not rotate again, still works.
Thanks in advance
If you want your application to stick in landscape mode you the blow code, the issue resides in iOS 5, many people face this issue
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
BOOL returningValue=NO;
if (interfaceOrientation==UIInterfaceOrientationIsPortrait(interfaceOrientation))
{
returningValue=NO;
}
else if(UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
returningValue=YES;
}
return returningValue;
}
Apparently, this is where I went wrong
For iOS 5
when setting the view of the main window of the application, one must use
[self.window addSubview: tabBarController.view];
Instead of (iOS6 only)
[self.window setRootViewController: tabBarController];
I am unsure how that changes everything, and the reason it won't work for iOS 5. Nevertheless, it worked.
Thanks everyone :)
So I'm creating and showing a custom window in my iOS app because I'm writing a dynamic alert view that also functions like a growl/toast alert. It works AWESOMELY in ios6 (Hopefully I can open source this baby and you can all check it out)
But anyway, when I run this in ios5, the window that my alerts exist on doesn't seem to rotate with the device/simulator. No matter what, my custom window stays in portrait mode.
The UIWindow is just a UIView subclass, so there's no nice 'shouldRotate' delegate method.
I'm kinda stumped on why this is happening in ios5 but not 6. Any help would be GREATLY appreciated ^_^
My window has a rootviewcontroller, which I completely forgot about. I just needed to implement
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return YES;
}
To get it to work.
:-D
It's usually not recommended two use multiple instances of UIWindow in one iOS app. The only valid reason to do so is to support external screens. You should use a UIView instead, ideally managed by a UIViewController.
I assume, (since you didn't provide any code, I can only assume) the reason why your window doesn't 'rotate' is, that it's simply not getting any notifications about device rotation. Only the keyWindow receives them by default.
I would highly recommend to redesign your app to use a properly managed UIView instead. If you desperately don't want that for some reason, you would have to register your instance of UIWindow to receive the UIDeviceOrientationDidChangeNotification and then (in the handler) evaluate what the new orientation is and change the window's frame accordingly (plus maybe other things that need to be done in response to the orientation change)