I have a tab bar application that contains navigation views in 2 of its tabs. I would like 1 view in the 1 navigation controller to allow landscape view but because of the nav bar in tab bar limitation I now have to allow landscape views for every single view in my app to make the tilt messages get passed to my app which I don't want.
I thought perhaps, on the views which shouldn't go to landscape, that there might be ways to either:
prevent the view change e.g. calling setOrientation:UIDeviceOrientationPortrait whenever the device goes landscape
or
giving the illusion that the view doesn't change e.g. presenting a modal portrait view over the rotated view
Anybody have any ideas or experience that they care to share? What is the best approach here? (I don't want to now have to design a landscape view for every view just to so that I can display a portrait & landscape view for 1 view)
I had to deal with the same problem recently and my solution is as follows:
within the UIViewController of the View that you want to be able to rotate add a Notification Handler for the UIDeviceOrientationDidChangeNotification
-(void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotateFromInterfaceOrientation)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
then of course you need to implement your didRotateFromInterfaceOrientation Method.
within that Method you can get the current orientation using
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
what I did next was evaluation the view i want to display, based on the orientation
switch (orientation) {
case UIDeviceOrientationLandscapeLeft:
NSLog(#"UIDeviceOrientationLandscapeLeft");
[self presentModalViewController:LandscapeView animated:YES];
break;
case UIDeviceOrientationLandscapeRight:
NSLog(#"UIDeviceOrientationLandscapeRight");
[self presentModalViewController:LandscapeView animated:YES];
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(#"UIDeviceOrientationPortraitUpsideDown");
[LandScapeview dismissModalViewControllerAnimated:YES];
break;
case UIDeviceOrientationPortrait:
NSLog(#"UIDeviceOrientationPortrait");
[LandscapeView dismissModalViewControllerAnimated:YES];
break;
case UIDeviceOrientationFaceUp:
NSLog(#"UIDeviceOrientationFaceUp");
break;
case UIDeviceOrientationFaceDown:
NSLog(#"UIDeviceOrientationFaceDown");
break;
default:
break;
}
}
I hope i could help a bit.
Related
I've tried setting the Info.plist 'View controller-based status bar appearance' to NO, I've tried calling
[[UIApplication sharedApplication] setStatusBarHidden:YES];
I've tried
-(BOOL)prefersStatusBarHidden{
return YES;
}
I've tried launching the picker with
[self presentViewController:picker animated:NO completion:^{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
And still, there is a status bar overlapping the camera controls. It's only there in iOS 7 though.
The status bar doesn't show up any where else in the app. I feel like I'm missing an important piece of the puzzle here, and no amount of reading about the View Controller or UIImagePickerController has helped me find said puzzle piece.
I'm hoping some one else has a little insight into this problem. Thank you.
EDIT: My desired effect is that the Status Bar shows up every in the app, except on the camera picker and a few other "outside" (Email related) view controllers we use.
If you want to keep ViewController-Based Status Bar Appearance, subclass UIImagePickerController and override prefersStatusBarHidden and childViewControllerForStatusBarHidden.
#interface NoStatusBarImagePickerController : UIImagePickerController
#end
#implementation NoStatusBarImagePickerController
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return nil;
}
#end
Try this :
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
in your appDelegate.
There's an additional setting you need to turn on, starting in iOS 7. In your app's Info.plist, add a line for View controller-based status bar appearance, a Boolean, and set it to NO.
The PsychoDad method works for me. I put the following
[[UIApplication sharedApplication] setStatusBarHidden:YES];
into the method viewWillDisappear of subclass of UIImagePickerController.
But the Alexandru Dranca method is better because in that way the status bar don't appear at all!
However I think this is a BUG of IOS 7...
"View controller-based status bar appearance" set to NO, works for me.
you should leave the
-(BOOL)prefersStatusBarHidden{
return YES;
}
and also add this
-(void)viewWillAppear:(BOOL)animated {
...
[self setNeedsStatusBarAppearanceUpdate];
...
}
I've been on this bug for repairing ToonPAINT for iOS7 and the thing that finally worked other than setting the two things in the Info.plist file (Status bar is initially hidden = NO; View controller-based status bar appearance = NO)
was to change the style of the status bar (even though I didn't want it shown at all); It was not enough to just hide the status bar; sounds like an iOS7 bug.
In the app delegate add:
-(void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
{NB .. UIStatusBarStyleBlackTranslucent is deprecated, probably use UIStatusBarStyleLightContent if trying this}
I think the answer to this question is "This is an iOS 7 bug". None of the methods here helped in our case, and several people have tried to fix this now.
I can't say what steps to reproduce this, but I've seen enough people out there with the same issue, that I think it's safe to say that this is in fact an iOS 7 bug. Most people can fix this problem with the multiple methods listed above. Rarely though, you can't fix it that way. I hope if you are reading this, you are not also one of those people.
This is what worked for me:
#implementation ViewController {
BOOL hideStatusBar;
}
- (void)showCamera {
UIImagePickerController *camera = [[UIImagePickerController alloc] init];
camera.modalPresentationStyle = UIModalPresentationCurrentContext;
camera.sourceType = UIImagePickerControllerSourceTypeCamera;
camera.delegate = self;
hideStatusBar = YES;
[self setNeedsStatusBarAppearanceUpdate];
[self presentViewController:camera animated:YES completion:nil];
}
-(BOOL)prefersStatusBarHidden{
return hideStatusBar;
}
I have a Navigation Controller based app. From my Root VC, I have one view that I present modally like this:
[self.navigationController presentModalViewController:shortcutsViewController animated:YES];
shortcutsVC contains a row of buttons, which take the user to various places in the app.
In iOS 4.3, this works:
UINavigationController *saveNavigationController = (UINavigationController *)self.parentViewController;
[saveNavigationController pushViewController:multipleListViewController animated:YES];
[saveNavigationController dismissModalViewControllerAnimated:YES];
[multipleListViewController release];
It does not work if the dismiss happens before the push.
In iOS 5, it doesn't work at all, regardless of which order the two commands are in.
By "not work" I mean that the button highlights briefly but the new VC is not displayed.
What is the right way to do this? Or am I doing something that was illegal all along and just happened to work in 4.3?
EDIT:
I got some offline help on this one.
The problem is that in iOS5, parentViewController has been renamed presentingViewController. Since I still need to support both 4.x and 5.x, I changed this
UINavigationController *saveNavigationController = (UINavigationController *)self.parentViewController;
to this
UINavigationController *saveNavigationController;
if ([self respondsToSelector:#selector(presentingViewController)]) {
saveNavigationController = (UINavigationController *)self.presentingViewController;
} else {
saveNavigationController = (UINavigationController *)self.parentViewController;
}
And all is happy now.
I have created a split view application that begins with a modal view splash page. The problem is that the modal view always launches in portrait mode, even if the ipad is in landscape. If I rotate the ipad a couple of times, it rotates appropriately. I have set UIInterfaceOrientation in my Info.plist, but it doesn't have any impace.
in didFinishLaunchingWithOptions, I am using the following code
...
[self.window addSubview:splitViewController.view];
SplashViewController *modalView = [[SplashViewController alloc] intiWithNibName:nil bundle:nil];
modalView.modalPresentationStyle = UIModalPresentationFullScreen;
[splitViewController presentModalViewController:modalView animated:YES];
...
Any suggestions on how I can ensure the modal view launches in landscape?
I think this is the better way to do it:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation==UIInterfaceOrientationLandscapeRight) {
return YES;
}
return NO;
}
I had a similar problem when using Matt Gemmell's MGSplitViewController. In my case, by trying to open a modal view controller in FormSheet mode from inside the detail view controller (that is the "right" pane in the UISplitViewController standard), my modal view was forcing interface rotation to portrait.
I found the solution by overriding the modal view controller -> shouldAutorotateToInterfaceOrientation: and letting him returning a NO:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return NO;
}
In this way when the modal is going to be presented, for some reason the OS tries to force it to portrait. By answering NO the view is no more rotated and everything works fine.
In the file from which you launch the modal view, you will want to change/override the following function. You can simply copy and paste the following code and you should be able to launch the modal view in portrait landscape mode:
- (BOOL)shouldAutoRotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Good luck.
EDIT: I said portrait mode instead of what I meant: landscape mode.
I would like to make popover view visible whenever user switches from landscape view to portrait view in UIsplitView of iPad. Although user can make it visible by clicking on bar button but I want this to be automated for portrait mode.
Inside " -(BOOL) shouldAutorotateToInterfaceOrientation" method, check for the device orientation.If it is portrait, then Present the popover as you do for making it visible when user clicks bar button.
All the best.
UISplitViewController sends messages to his delegate (UISplitViewControllerDelegate). You can implement this delegate methods to show the popover. You can do something like this in your "detail controller" code:
#pragma mark -
#pragma mark UISplitViewControllerDelegate implementation
- (void)splitViewController:(UISplitViewController*)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem*)barButtonItem
forPopoverController:(UIPopoverController*)pc
{
[barButtonItem setTitle:#"Your 'popover button' title"];
self.navigationItem.leftBarButtonItem = barButtonItem;
}
- (void)splitViewController:(UISplitViewController*)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
self.navigationItem.leftBarButtonItem = nil;
}
The accepted answer (using shouldAutorotateToInterfaceOrientation) doesn't work for me. It either has rotation artifacts (in the 4.2 and 5.0 iPad simulators) or only shows at startup and never again in subsequent rotations (the 4.3 simulator). What I did instead was to create a little helper function:
- (void)showPopoverInPortrait {
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait) {
[self.masterPopoverController presentPopoverFromBarButtonItem:self.navigationItem.leftBarButtonItem
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
and call this within - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation and - (void)viewDidLoad to also handle on startup.
I am building my first iPad application. And one of my requirements is to deal with UISplitviewcontroller and UINavigationController.our proposed view hierarchy is (LoginView) ->UINavigationView(LandingView + CollectionView)->UISplitViewcontroller( DetailsView).Our app supports only landscape mode
I am referring this SO Question( and GILT app as well), as a newbi its hard for me to get it done based on that description.
So my questions are
1) How can I achieve same thing,if somebody can give small code snippets or reference to tutorial
2) As per Apples HIG, UISplitviewcontroller should be rootviewcontroller,what if it not. Will apple reject my app.(apparently GILT group has been approved)
3) I found MGSplitViewController , can I use that one not as root?
Any help would be appreciated. As a newbi i hope my question is genuine
If you want to use the out-of the box splitView it must be root; any hokeary-pokery here will either break apples guidelines or manifest very odd behaviour.
The MGSplitViewController is completely custom implementation of a SplitViewController. Its very good if you need that sort of thing, but some of the features are based round the fact that our app will be orientating.
Alternatively you could make your own. I have done this more than once and is easier than it sounds.
(LoginView)
->UINavigationView(LandingView + CollectionView)->UISplitViewcontroller(
DetailsView).
Based on an out-of-the-box UISplitView, I would suggest:
Make the splitView the root View.
Pop (not animated) a full screen Modal as soon as the app starts and allow the
user to navigate the loginView,
LandingView and collectView in this; i also recommend using a navController here.
Once the user is ready to proceed to
the splitView, populate the
splitView's rootView Controller and
DetailViewController with whatever
you want then ,animate the Modal out.
Dave does have a point, but i would look at it from the point of view that you are removing the choice of orientation from the user; removing standard choices (like supported orientations) because the designer assumes some configuration is more efficient will only annoy some users.
I did it by making a method in my detailViewController:
-(void)popHomeScreen:(BOOL)animated//OPENS THE HOMESCREEN IN A MODAL DISPLAY
{
firstRun=NO;
//myViewControllerForPopOver init here
myViewControllerForPopOver.modalPresentationStyle = UIModalPresentationFullScreen;
myViewControllerForPopOver.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;//simple anime
if (animated) [self presentModalViewController:myViewControllerForPopOver animated:YES];
else [self presentModalViewController:myViewControllerForPopOver animated:NO];
}
Then Call it in the detailViewControllers ViewDidAppear method:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(firstRun)[self popHomeScreen:NO];
}
//YOU WILL ALSO NEED TO MAKE 'firstRun=YES' in viewDidLoad
//firstRun is a BOOL
As for the root disappearing (leaving a big black space)..thats a known bug. though i thought for a while that it was caused by ordering an animation while/just before it was going to draw itself.
Also, i found that if i popped a full screen modal over the splitView then popped another form based modal (for example) quickly after it the full screen modal didn't draw properly.
SplitViews are like your mother-in-law, you don't really like them and when you have to use them you have to tip-toe round the landmines.
Try this, it works for me
1) in app delegate's didFinishLaunchingWithOptions make your login view to root view
self.window.rootViewController = self.loginViewController;
also, init the split view as the template do ( but not add to self.window)
MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:#"MasterViewController_iPad" bundle:nil] autorelease];
UINavigationController *masterNavigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease];
DetailViewController *detailViewController = [[[DetailViewController alloc] initWithNibName:#"DetailViewController_iPad" bundle:nil] autorelease];
UINavigationController *detailNavigationController = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease];
self.splitViewController = [[[UISplitViewController alloc] init] autorelease];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController ,detailNavigationController, nil];
2) add a swap view controller in app's delegate, which swap the root view to split viewcontroller.
-(void)swapToViewControllers:(RootViewControllerType)viewType
self.window.rootViewController = self.splitViewController;
3) invoke the swapToViewControllers in your login view.
Keep in mind that the HIG strongly encourages you to support all orientations. Unless you have a very, very good reason to support landscape only, you'll probably be rejected.
See page 19 of the HIG: http://developer.apple.com/library/ios/documentation/General/Conceptual/iPadHIG/iPadHIG.pdf
Modified Ryan CY's version with storyboard.
Put this code to login controller.
1. Set storyboard id of UISplitViewController to SplitViewController;
2. Set UISplitViewController delegate and save instance
UISplitViewController* splitController = [self.storyboard instantiateViewControllerWithIdentifier:#"SplitViewController"];
UINavigationController* navigationController = [splitController.viewControllers lastObject];
splitController.delegate = (id)navigationController.topViewController;
3. Change rootViewController after login
self.view.window.rootViewController = splitController;