I am creating an application with Landscape Right orientation. For that I set the Initial interface orientation property in info.plist file. Then in every view I handled
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return(interfaceOrientation==UIInterfaceOrientationLandscapeRight);
}
It works fine in simulator but in device its behave differently.
My first view is in proper orientation. There is is popover which display another view that comes in portrait mode. Still my status bar is in Landscape Right..
For navigating from one view to another view I am using..
self.window.rootViewController = self.myNav;
I have multiple navigation Controller and adding those using the upper code.
I am not getting what is the problem.
Any help will be appreciated.
EDIT: I had used
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
I never get this issue in simulator but getting this in device and not every time. I have used Supported interface orientations (iPad) too and set Landscape (right home button) value for item0.
Thanks In advance
You need to set the "Simulated Metrics > Orientation" property of your top view (in all your xib files) to be "Landscape". The default is portrait.
The question was answered pretty well here - Landscape Mode ONLY for iPhone or iPad .
I also have an app that like yours only supports UIInterfaceOrientationLandscapeRight. I haven't run into any orientation issues so far. I only have one UIViewController under the window. This UIViewController has its own UITabBar that I use to change pages. So, we change pages differently. I set my UIViewController using the rootViewController property of the window just like you, but again I only have one.
Also, I never had to do anything like the [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] call that you included. Since you only support LandscapeRight orientation, you shouldn't care to be notified of changes.
EDIT
I created a sample project, which I can post if necessary, and I think I may know your problem. First - do you only encounter the sizing issue inside popovers? If so, then I don't think orientation is throwing you off but the popover itself. My sample project has 3 view controllers. The first loads the second by changing the window's rootViewController. That worked fine. The second view controller has a button to open a popover. This will show up wrong unless you set the contentSizeForPopover on the view controller as shown below:
- (IBAction) showVC3InPopover
{
UIViewController * vc3 = [[VC3 alloc] init];
/** Set the contentSizeForViewInPopover property to determine how
large the view will be in the popover! You can do this in the
init method(s) of the view controller if you prefer, it's just
easy to do here */
vc3.contentSizeForViewInPopover = vc3.view.frame.size;
[_popover release], _popover = nil;
_popover = [[UIPopoverController alloc] initWithContentViewController:vc3];
[vc3 release];
[_popover presentPopoverFromRect:_showPopoverButton.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
See if this fixes your problem. If it does, there are other ways to control the popover size, but this is just what I typically do. However, just know that the PopoverController ignores the size of the view in the viewcontroller you load.
How many views (or viewControllers) you have? You might need to implement this orientation logic in all those views??
Related
I am working on a landscape view for a currently existing application. I believe I have autoRotate, supported interface, etc set up correctly, I am actually reusing code that works with a much simpler app. However when the simulator is rotated into landscape mode, the correct view loads, but the status bar and view stay with the short edge of the iPad. I've attached a screenshot and code. Is the problem with a view controller higher up in the chain, or the appdelegate? I've traced the called controllers in the debugger and it appears they are dismissed once this page is loaded. I am fairly new-ish to obj-c so it is possible this is something simple I am missing, but I have checked all attributes for the .xib file and everything looks copasetic.
Some of the code:
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;// | UIInterfaceOrientationMaskPortraitUpsideDown;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return NO;
}![enter image description here][2]
-(void)orientationChanged{
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if ((interfaceOrientation==UIInterfaceOrientationPortrait)||(interfaceOrientation ==UIInterfaceOrientationPortraitUpsideDown)){
self.view = self.portraitView;
} else {
self.view = self.landscapeView;
}
}
EDITS - This problem occurs on both iOS 7.1 and 6.1 and this is the first time any screen in the app supports a landscape view. To clarify the views, the portrait and landscape views are separate Views in single .xib file. The file owner class is set to the correct view controller class, and the parent view controller, a sales screen, should not rotate. It does not have a landscape view, but even with its autoRotate methods set to return YES the subview does not orient correctly.
Like 0x7ffffffff already said you need to allow rotation in your shouldAutorotate functions. Also you need to set up the supported rotation directions in your project setup.
First go to your project's settings:
Next you need to select all the orientations you want to support:
Another that is very important: ONLY the root View Controller will receive rotation events. If you nest a View Controller inside a View Controller then that nested Controller will not receive those events unless you wire them up manually from the parent. That's why I usually don't nest ViewControllers but use ad-hoc NSObjects or UIView implementations for nested views.
Last but not least: make sure your device is not rotation-locked: http://www.iphonefaq.org/archives/972915
The problem was occurring because a subclass of the customerView was not receiving the rotation notification. After tracking that class down it was a matter of setting up the NSNotificationCenter for orientation changes and then allowing autoRotation and supprotedInterfaceOrientations.
I have an app with an UITableView at the home screen. I made this view to always be in landscape orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
If the user picks a row he will be taken to a UIPageViewController view. This second view can rotate to landscape and portrait. The problem is when I am in portrait mode on the UIPageViewController and I press the go back button, the first view which is supposed to be always in landscape mode now is in portrait. After I rotate it, it gets into landscape and stays there.
I was wondering maybe if there is a way to make my home screen go automatically into landscape when I go back to it.
Try the following
Create the view of your main screen in app in interface builder in Landscape mode.
Create uiview oultlet in interface class and connect it to above view.
IBOutlet UIVIew *myView;
Then in the viewDidLoad method set this
self.view = self.myView;
If you want to make a screen in a particular orientation then you can create a CustomNavigation controller and then present it in your app. You have to only return supportedInterfaceOrientations in this. If you want more detail and sample code click here.
Call shouldAutorotateToInterfaceOrientation manually when you go back. You can not force a "real" orientation change, that's a OS thing.
As said in the view controller programming guide, you can have a alternate landscape interface and before coming to home screen from any other view, you can check the orientation and push the corresponding interface onto the screen
Read this SO question and answer for better understanding of launching an app in landscape.Also go through above apple programming guide which i pointed to.
If you use the UINavigationViewController methods(pushViewController:animated: and popViewControllerAnimated:), the views will inherit the previous view's orientation.
On the other hand, if you use presentModalViewController:animated: and dismissModalViewControllerAnimated: methods, everything works perfectly. Hope this helped!
Use this, change the UIInterfaceOrientationLandscapeLeft to required orientation type as UIDeviceOrientationPortrait, UIDeviceOrientationLandscapeLeft etc.
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
I have setup a new iPad project to only support UIInterfaceOrientationLandscapeRight.
In my App Delegate I add a RootViewController to the window's rootViewController.
In this UIViewController (RootViewController) I have the following:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
I have also tried with:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
However, I am not able to get the correct dimensions for my app when I create and add subviews based on the dimensions of my view controller's view.
If I output self.view.frame for my view controller I get {{0, 0}, {768, 1024}}, but I would like {1024, 768} instead. If I can't when are the dimensions correct so I can create my views with them in mind?
Sorry if this has been asked a billion times, I've browsed lots of SO questions, but nothing has solved my issue.
I was running into the same issue, and what Ash Furrow said above seems to be correct; the orientation is set after viewDidLoad is called.
I was creating an iPad app that works in all orientations, but only the portrait orientations were getting set up correctly in my root UIViewController. In order to make the views layout correctly in landscape, I had to make sure the autoresizing masks on all my subviews was set to allow the view to adjust to landscape behind the scenes before being displayed to the user.
For example, I had a UIImageView that was the same size as the UIViewController's UIView. In order to get it to adjust correctly when rotating to landscape:
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
backgroundImageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight);
[self.view addSubview:backgroundImageView];
Now the UIViewController can be configured in portrait orientation in the viewDidLoad method and rotate nicely to the portrait orientation before being displayed to the user.
EDIT:
Looks like the interface orientation is being set already in viewDidLoad
p (UIInterfaceOrientation)[self interfaceOrientation]
(UIInterfaceOrientation) $1 = UIInterfaceOrientationLandscapeRight
Here's my theory: interfaces on the iPad are, by default, 1024x768 if that have a status bar, which yours does. I believe that, even though the interface orientation is correct, it's not updating the view geometry until after viewDidLoad. I believe it has a very good reason for that.
If you look at the UIViewController Life Cycle docs, viewDidLoad is called as part of the set up of the view controller. After the view is loaded, willAnimateRotationToInterfaceOrientation: duration: is called to let your view controller know it's geometry is changing.
It's not so much an answer as an explanation. Hopefully this will help you architect a solution to get around this problem.
Begin answer that doesn't actually work:
In the info.plist for your project, open the "Supported Interface Orientations" option and delete the interface orientations you don't want to support. By default, all are supported:
That should clear it up.
I have a simple solution: the Apple default app templates work with XIBs for the main view controller. To solve your problem just open the main viewcontroller XIB and set orientation to "landscape".
As I usually don't use XIBs and create all UI elements programmatically. That's why I had the same problem in previous projects that really drove me crazy. I then solved it by hard-coding the frame width & height value - not the nice way.
As has been reported in other questions here on SO, iOS 5 changes how rotation callbacks for split view controllers are sent as per this release note. This is not a dupe (I think), as I can't find another question on SO that deals with how to adjust split view controller usage in iOS 5 to cope with the change:
Rotation callbacks in iOS 5 are not applied to view controllers that
are presented over a full screen. What this means is that if your code
presents a view controller over another view controller, and then the
user subsequently rotates the device to a different orientation, upon
dismissal, the underlying controller (i.e. presenting controller) will
not receive any rotation callbacks. Note however that the presenting
controller will receive a viewWillLayoutSubviews call when it is
redisplayed, and the interfaceOrientation property can be queried from
this method and used to lay out the controller correctly.
I'm having trouble configuring the popover button in my root split view controller (the one that is supposed to show the left pane view in a popover when you're in portrait). Here's how my app startup sequence used to work in iOS 4.x when the device is in landscape mode:
Install split view controller into window with [window addSubview:splitViewController.view]; [window makeKeyAndVisible];. This results in splitViewController:willHideViewController:withBarButtonItem:forPopoverController: being called on the delegate (i.e. simulating a landscape -> portrait rotation) even though the device is already in landscape mode.
Present a fullscreen modal (my loading screen) which completely covers the split view underneath.
Finish loading and dismiss the loading screen modal. Since the device is in landscape mode, as the split view controller is revealed, this causes splitViewController:willShowViewController:invalidatingBarButtonItem: to be called on the delegate (i.e. simulating a portrait -> landscape rotation), thereby invalidating the bar button item, removing it from the right-side of the split view, and leaving us where we want to be. Hooray!
So, the problem is that because of the change described in that release note, whatever happens internally in iOS 4.3 that results in splitViewController:willShowViewController:invalidatingBarButtonItem: being called no longer happens in iOS 5. I tried subclassing UISplitViewController so I could provide a custom implementation of viewWillLayoutSubviews as suggested by the release note, but I don't know how to reproduce the desired sequence of internal events that iOS 4 triggers. I tried this:
- (void) viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
UINavigationController *rightStack = [[self viewControllers] objectAtIndex:1];
UIViewController *rightRoot = [[rightStack viewControllers] objectAtIndex:0];
BOOL rightRootHasButton = ... // determine if bar button item for portrait mode is there
// iOS 4 never goes inside this 'if' branch
if (UIInterfaceOrientationIsLandscape( [self interfaceOrientation] ) &&
rightRootHasButton)
{
// Manually invoke the delegate method to hide the popover bar button item
[self.delegate splitViewController:self
willShowViewController:[[self viewControllers] objectAtIndex:0]
invalidatingBarButtonItem:rightRoot.navigationItem.leftBarButtonItem];
}
}
This mostly works, but not 100%. The problem is that invoking the delegate method yourself doesn't actually invalidate the bar button item, so the first time you rotate to portrait, the system thinks the bar button item is still installed properly and doesn't try to reinstall it. It's only after you rotate again to landscape and then back to portrait has the system got back into the right state and will actually install the popover bar button item in portrait mode.
Based on this question, I also tried invoking all the rotation callbacks manually instead of firing the delegate method, e.g.:
// iOS 4 never goes inside this 'if' branch
if (UIInterfaceOrientationIsLandscape( [self interfaceOrientation] ) &&
rightRootHasButton)
{
[self willRotateToInterfaceOrientation:self.interfaceOrientation duration:0];
[self willAnimateRotationToInterfaceOrientation:self.interfaceOrientation duration:0];
[self didRotateFromInterfaceOrientation:self.interfaceOrientation];
}
However this just seems to cause an infinite loop back into viewWillLayoutSubviews :(
Does anyone know what the correct way to simulate the iOS4-style rotation events is for a split view controller that appears from behind a full-screen modal? Or should you not simulate them at all and is there another best-practices approach that has become the standard for iOS5?
Any help really appreciated as this issue is holding us up from submitting our iOS5 bugfix release to the App Store.
I don't know the right way to handle this situation. However, the following seems to be working for me in iOS 5.
In splitViewController:willHideViewController:withBarButtonItem:forPopoverController:, store a reference to the barButtonItem in something like self.barButtonItem. Move the code for showing the button into a separate method, say ShowRootPopoverButtonItem.
In splitViewController:willShowViewController:invalidatingBarButtonItem:, clear that self.barButtonItem reference out. Move the code for showing the button into a separate method, say InvalidateRootPopoverButtonItem.
In viewWillLayoutSubviews, manually show or hide the button, depending on the interface orientation
Here's my implementation of viewWillLayoutSubviews. Note that calling self.interfaceOrientation always returned portrait, hence my use of statusBarOrientation.
- (void)viewWillLayoutSubviews
{
if (UIInterfaceOrientationIsPortrait(
[UIApplication sharedApplication].statusBarOrientation))
{
[self ShowRootPopoverButtonItem:self.barButtonItem];
}
else
{
[self InvalidateRootPopoverButtonItem:self.barButtonItem];
}
}
I'm having a hard time understanding why the following is happening (and how to fix it).
I've created an application using the split-view based application.
I've added a UiBarButtonItem called showTheModal which calls this method found in RootViewController.m:
- (IBAction)showTheModal:(id)sender {
theModalController.modalPresentationStyle = UIModalPresentationFullScreen;
theModalController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:theModalController animated:YES];
if ([detailViewController popoverController] != nil)
[[detailViewController popoverController] dismissPopoverAnimated:YES];
The BarButtonItem of course, is shown at the bottom of the Default Root Controller (left side of the of the split view in landscape) or at the bottom of the popup (if in landscape).
The modal view is dismissed by a button placed in a toolbar. It calls the following:
[self dismissModalViewControllerAnimated: YES];
The problem I'm having is if rotate the screen, while the modal is up. Here is what happens in different scenarios (start refers to the orientation when the showTheModal button is hit, end refers to the orientation when I hit the dismissModal button).
1)Start landscape, end landscape: Everything appears fine. willHideViewController and willShowViewController methods are not called in the RootViewController (as expected)
2) Start landscape, end portrait: UI appears fine. willHideViewController is run TWICE (WHY?)
3) Start portrait, end portrait: UI appears fine. willHideViewController is run once (as expected)
4) Start portrait, end landscape: The 'Root List' button remains in the detail view (right side of the split view. Neither willHideViewController and willShowViewController are invoked (WHY??)
Any thoughts as to why #2 and #4 don't behave quite the expected way?
I've had exactly the same problem (#4, above). I worked around it using viewDidAppear:animated, and then checking the height of the view to see if it is in landscape vs. portrait. (Yuck, gag, etc.) I'm not satisfied at all with that "solution".
Possibly related: I've noticed that the button in portrait mode is slow to disappear after rotating to landscape, i.e. the button appears for a second after the rotation finishes. However, in Mail.app, the "Inbox" button disappears as soon as the rotation starts. Is Apple doing things differently than they recommend in their docs? Perhaps there is a more efficient way to show/hide the master view button?
Unfortunately this is not a bug. It appears to be an expected behavior.
I found this in iOS Release Notes for iOS 5.0, in "Notes and Known Issues" section:
Rotation callbacks in iOS 5 are not applied to view controllers that
are presented over a full screen. What this means is that if your code
presents a view controller over another view controller, and then the
user subsequently rotates the device to a different orientation, upon
dismissal, the underlying controller (i.e. presenting controller) will
not receive any rotation callbacks. Note however that the presenting
controller will receive a viewWillLayoutSubviews call when it is
redisplayed, and the interfaceOrientation property can be queried from
this method and used to lay out the controller correctly.
For diagnostics, have you tried dismissing the popover view first? Or logging who is calling the method by printing (id) sender?
I was having the same exact problem.
In answer to (2), it appears to be a bug. I noticed that when a modal view is pushed over a splitview, the orientation messages are queued up somewhere and not processed until the modal view is dismissed and the splitview is visible but I would still expect to only get one callback.
For (4), this too appears to be a bug. Fortunately, the didRotate... events still get through, so my solution was to subclass UISplitViewController and explicitly call the delegate's willShowViewController method in this case:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
//Work around a bug where UISplitViewController does not send
//willShowViewController after a modal is presented in portrait
//but dismissed in landscape.
UIInterfaceOrientation orientation = self.interfaceOrientation;
if ( (orientation == UIInterfaceOrientationLandscapeLeft )
|| (orientation == UIInterfaceOrientationLandscapeRight) )
{
UINavigationItem* item = [detail.navigationBar.items objectAtIndex:0];
UIBarButtonItem* barButtonItem = [item leftBarButtonItem];
[super.delegate splitViewController:self willShowViewController:master invalidatingBarButtonItem:barButtonItem];
}
}
Here, "master" is an IBOutlet that refers to the master view controller (left hand side) of the splitview and "detail" is an IBOutlet for the detail view controller (right hand size).
Note that in my case, the detail view is a UINavigationController. You may require different code to get the barButtonItem from your view controller.
Also, this has the side-effect of calling willShowViewController twice for normal rotation, but that is not an issue in my case.
I think that this is a bug that needs to be reported to Apple Development.
I worked around part of this issue by presenting my modal view using the UIModalPresentationPageSheet format.