What is up to date best solution for rotating inner views when dealing with UITabBarController? I'm playing around with a standard app view hierarchy: In my main app delegate file, I'm creating UITabBarController, then, I'm creating UINavigationController, filling it with a UITableViewController (with instantiated custom subclass), and adding that UINavigationController to the first tab bar item. Now, I need UITableViewController to autorotate. I know that I need to implement shouldAutorotateToInterfaceOrientation in all view controllers, therefore, I implemented it in my custom UITableViewController subclass implementation file:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
But the UITableViewController is not rotating :( .Now, I understand that there are UINavigationController and UITabBarController objects, that are sitting above my TVC but those two are instantiated directly not through a subclass, so there is no place where to return yes for autorotation. However, I'm able to solve this problem by subclassing a UITabBarController and implementing shouldAutorotateToInterfaceOrientation method in its implementation file. But I have read, that this is a non-recommended approach and my conscience feels bad :) Another working solution is to implement a category for UITabBarController (http://stackoverflow.com/questions/1269704/uitabbarcontroller-morenavigationcontroller-and-the-holy-grail-of-device-rotatio)...
So, these two solutions are the only ones that I was able to apply. Are there any other "out of the box" solutions, for example, setting some property on the UITabBarController or smth?
In the class which you either subclass UITableViewController or implement UITableView in UIViewController, you need to set autoresizingMask for tableView. It is something like
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
and TableView should be ok and rotated properly.
Hope this help :)
Related
I have created a custom TabBarController that inherits from UIViewController (NOT UITabBarController because it is not intended for subclassing). Everything works fine except the orientation support.
My TabBarController is set as the rootViewController on my UIWindow and contains an internal array of UIViewControllers. In my case I have added UINavigationControllers as the root of each tab.
When pushing a new UIViewController to any of my UINavigationControllers in my TabBarController I get a call to shouldAutorotateToInterfaceOrientation, this is all fine because here I can set which UIViewController should support which orientation (as described in the Apple documentation).
However when I go back by pressing the back button in my UINavigationBar I do not get a call to shouldAutorotateToInterfaceOrientation hence the view we display will end up in the wrong orientation.
I have done a quick test by replacing my custom TabBarController with a UITabBarController and I get the call to shouldAutorotateToInterfaceOrientation when pressing the back button so there must be something wrong here but I cannot figure out what.
Why don't I get the calls to shouldAutorotateToInterfaceOrientation? Is the UITabBarController doing something I have missed?
Has anybody here experienced the same problem? Do you have any ideas that might be worth trying because I have run out of ideas.
EDIT
This issue is resolved by adding each UIViewController within the TabBarController as a child using the iOS5 container view controller.
Add the UIViewController as a child with this method: addChildViewController Then override automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers and return YES.
Now you should get the shouldAutorotateToInterfaceOrientation callbacks in your UIViewControllers
I have seen such problems before, and it always boiled down to adding the -shouldAutorotateToInterfaceOrientation: method to all view controllers, not just the topmost one as you might expect.
(and if you're lazy, just add a category to UIViewController)
a custom tab bar controller I am using applies the ViewController or UINavigationController like this: UIViewController* viewController = [data objectForKey:#"viewController"];
I dont knw exactly how it works but "viewController" comes out as a UINavigationController. Next, the custom tab bar controller class adds a tag like so, viewController.view.tag = THE_TAG;
Retrieving the controller is uses UIView* currentView = [self.window viewWithTag:SELECTED_VIEW_CONTROLLER_TAG];
This part is where I get confused because now when I nslog this
"currentView" I get a UILayout etc... instead of my UINavigationController. I'm assuming it applied the tag to the UIView that contained the nav controller?
How do I reference the UINavigationController within this UIView?
In the above what is THE_TAG, and can you confirm that it is unique (i.e. not zero, and not matching something being used elsewhere by the same mechanism)?
I'd be pretty wary overusing tag since there is no easy way to gaurentee globally unique tags, and when using something like self.window viewWithTag you could see just about every view in the app being checked.
It seems like you have a view and a viewController confused. A UINavigationController is a subclass of UIViewController. It is not a subclass of UIView. UIViewControllers do have a property which is a UIView class. It is probably this property that you are accessing when you use viewWithTag: . So maybe, when you use that method, you are not accessing the UINavigationController but the UINavigationController's view property (which is actually something you should probably not be messing with.)
I would like to subclass UIViewController with multiple nibs.
For example:
BaseViewController has a nib with a label, and is subclassed from UIViewController
SecondViewController has a nib with a button, and is subclassed from BaseViewController.
SecondViewController should also have the label from the BaseViewController.
I have searched an not found any tutorials on this, only tutorials with ways to add UIView's to UIViewControllers, so I'm not sure this is even possible.
Can anyone explain how to do this, or point me in the direction to the resources that show how to do this (or if it's even possible at all)?
I am using a hierarchy of controllers and it works just fine. As long as your correct match nibs to properties at each level, there is no reason you cannot define parent controllers in a hierarchy.
I don't think that what you are asking can be done. A view can be loaded from only one nib. You could have the base view controller set up with a nib, and descendant view controllers do additional setup in viewDidLoad.
I have a subclass of UITabBarController which i am using so that i can rotate to use my app in landscape too.
How would i go about rotating my UI and getting each view controller to use a landscape view xib?
I have always just written apps before where returning YES for shouldAutorotate... handles it automatically for me... this isn't the case here now, as i'm using a custom view.
Thanks.
You don't need to subclass UITabBarController to get the autorotation behavior. Instead what you should do is have ALL the UIViewControllers that appear in your UITabBarController return YES for shouldAutorotateToInterfaceOrientation:. If even one of them does not, the UITabBarController will not autorotate.
As for the custom view, it is associated with a UIViewController, right? If so, then if your custom view implements layoutSubviews using the current view bounds to lay out all the subviews, then it should autorotate correctly as well.
Greetings! Here's the scenario.
Starting with a navigation controller (and no tab bar is present - it is hidden from a previous view controller push), I init a new view controller and push it onto the nav controller stack. This new VC contains a lonesome UIView into which I programmatically add a UIScrollView with the same frame. (I wanted to avoid the UIView, but this was the only way I could get self.view to be assigned to something. I suspect casting a UIScrollView to UIView in viewDidLoad is not advisable.)
So now we have a nav bar, and a scroll view. I've set it up to scroll through some images (big surprise, I know!), and that works just fine. Now I want this to support autorotation. So I respond in the VC like so:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
Compile and run. Aaaand ... nothing. Obviously I've done something wrong.
Now, I've already read the post regarding UINavigationController and autorotation, and I get the sneaking suspicion that I'm going about this the wrong way, and making it way more complicated than necessary.
There's got to be a better way to present a UIScrollView that supports autorotation. Perhaps the Nav Controller is getting in the way, but I'm not sure how to get around it.
Ideally, I'd like something without any kind of nav bar showing. Instead, we have a toolbar/status bar that appears/hides from the top (like you see when playing video). If the nav bar must remain - or if that's REALLY a shorter-height nav bar I'm seeing when playing video vs. a toolbar, however do I get the thing to rotate around? The thing is, I only want it to rotate in this particular mode, when viewing the pix. Not at any other time.
Dare I try using a modal VC? (Yeccch - no, that can't be right either. Plus it has a nav bar anyway.)
You can solve this without subclassing by creating a UITabBarController category.
Here is the category implementation which handles my case where I have anonymous UINavigationControllers associated with my tabs and custom UIViewController subclasses as the root view controllers of the UINavigationControllers:
#implementation UITabBarController (Rotation)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([self.selectedViewController isKindOfClass:[UINavigationController class]]) {
UIViewController *rootController = [((UINavigationController *)self.selectedViewController).viewControllers objectAtIndex:0];
return [rootController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#end
I also have a category on UINavigationController which defaults to returning YES. So, the default behavior is to enable rotation and I can return NO from shouldAutorotateToInterfaceOrientation:interfaceOrientation for just the controllers and orientations for which I wish to disable rotation.
Yep - it was easier than I thought!
True, no tab bar is visible ... but this is still, at the core, a tab bar-based app.
Unless the UITabBarController allows autorotation, all bets are off for any other views. So, it's simply a matter of subclassing UITabBarController and responding appropriately to shouldAutorotateToInterfaceOrientation: (vs. making it part of the App Delegate as Xcode does by default).
How do you do that? Glad you asked. Follow these steps only if you've created a tab bar controller app using the Xcode defaults. (BACKUP your work before trying this! Disclaimer disclaimer, yadda yadda.)
Create a new UIViewController subclass (we'll call it VC). Adjust it to be a subclass of a UITabBarController with an explicit delegate of UITabBarControllerDelegate.
Transplant all the Tab Bar delegate bits from your App Delegate into this new VC.
In your new VC's viewDidLoad method, add self.delegate = self; to the end.
In MainWindow.xib (or wherever your tab bar controller and tab bar are defined), pick your Tab Bar Controller object and go to the Identity Inspector (Cmd-4). Change the class to your new VC instead of the standard UITabBarController class.
Now we're in business. Just add this to the new VC source:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES; // Adjust to taste
}
Why the delegate stuff? The IB file's owner is UIApplication, and I couldn't tie the delegate to my new VC via IB. Since I want the chance to respond to delegate methods, I added it in explicitly. If you don't need that, it's OK to leave it out. (If this can be done in IB, someone please chime in!)
The only remaining trick is setting this to YES selectively. You may not want to support autorotation all of the time (as is my case). Here's how I do it. First, I change that newly-added method (from above) ... to this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
Now I can respond to this same method from any of my app's VCs, and it will bubble up to the Tab Bar controller! For instance, in my app, I have a VC that I only want to show in Portrait, so I respond like so:
return (interfaceOrientation == UIInterfaceOrientationPortrait);
However, this same VC can take the user to a photo gallery, for which I do want to allow some rotation. In that VC's autorotate method, I respond differently:
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
Now the gallery view will allow autorotation to all orientations except upside-down portrait, plus when I go back up the view controller chain, the orientation reverts to portrait. :)
It can be simpler still:
1) Subclass UITabBarController and implement the shouldAutorotate... as you described (second code snippet)
2) Change your xxxAppDelegate.h to have the class of the UITabBarController changed to the subclass you just created. (use #import YourNewTabBarController.h)
3) In MainWindow.xib change the class of the tab bar controller to your new class.
Presto!
PS: YourNewTabBarController should ONLY implement the shouldAutoRotate....
Remove all other (auto generated) stuff.