I've got a UITabBarController which contains a UINavigationController. Within the visible UIViewController, I'm creating a UITableView programatically as follows:
self.voucherTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
self.voucherTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
However, the UITabBar is overlapping the UITableView.
When I output the height of the [[UIScreen mainScreen] applicationFrame], it returns 460.00 whereas it should be 367.00.
In Interface Builder, I'm using the 'Simulated Metrics' which automatically sets the height of the view to 367.00.
Is there something I'm missing, no matter what I try I can't see to get the 367.00 height that I need.
As a temp fix, I've set the frame of the UITableView manually, this isn't really ideal so it would be nice to work out why this isn't working:
self.voucherTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 367) style:UITableViewStylePlain];
You should use self.view.bounds rather than [[UIScreen mainScreen] applicationFrame] as the last one returns you the whole screen frame while self.view.bounds provides you with your view bounds wich seems what you are searching for.
You should add the UINavigationController instance to the UITabBarController and then add a table view controller to the rootViewController property of the UINavigationController instance which should make your life a lot easier.
As a simple example of this, create an empty window-based application (the templates make this a lot more confusing than it really is).
Add your UIViewController/UITableViewController subclasses to the project then use this code as a guide to setting up your project. This code is in your AppDelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// create our table view controller that will display our store list
StoresViewController *storeListController = [[StoresViewController alloc] init];
// create the navigation controller that will hold our store list and detail view controllers and set the store list as the root view controller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:storeListController];
[navController.tabBarItem setTitle:#"TableView"];
[navController.tabBarItem setImage:[UIImage imageNamed:#"cart.png"]];
// create our browser view controller
BrowserViewController *webBrowserController = [[BrowserViewController alloc] init];
[webBrowserController.tabBarItem setTitle:#"WebView"];
[webBrowserController.tabBarItem setImage:[UIImage imageNamed:#"web.png"]];
// add our view controllers to an array, which will retain them
NSArray *viewControllers = [NSArray arrayWithObjects:navController, webBrowserController, nil];
// release these since they are now retained
[navController release];
[storeListController release];
[webBrowserController release];
// add our array of controllers to the tab bar controller
UITabBarController *tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:viewControllers];
// set the tab bar controller as our root view controller
[self.window setRootViewController:tabBarController];
// we can release this now since the window is retaining it
[tabBarController release];
[self.window makeKeyAndVisible];
return YES;
}
In the code sample above the BrowserViewController is a subclass of UIViewController and the StoresViewController class is a subclass of UITableViewController. The UITabBarController and UINavigationController instances are created programmatically and added to the window.
By subclassing the UITableViewController class you avoid having to create a UITableView instance programmatically and get most everything you need out of the box.
When you need to push a detail view onto the UINavigationController instance's stack, you just have use something similar to this:
[self.navigationController pushViewController:YourDetailViewControllerInstance animated:YES];
This will add the detail view UIViewController subclass to the UINavigationController instance's view hierarchy for you and animate the transition.
Lots of controllers in this, but it's totally worth it and will avoid a lot of the problems you're experiencing as this method allows the views to manage resizing and take toolbars/navigation bars into account all by themselves.
Related
I have a UINavigationController as my rootViewController, which contains a UIViewController (which I will call projects for the sake of this discussion). In Projects, I have a button, which when clicked, I want to load a UISplitViewController - preferably sliding up from the bottom, although this is just a nice-to-have feature.
In the UISplitViewController, I have a "Close" button which I want to remove the UISplitViewController re-showing Projects.
From what I have read, UISPlitViewControllers must be the rootViewControllers. With that in mind, my code so far is as follows.
AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
...
// LOAD THE PROJECTS PANEL ROOT VIEW CONTROLLER INTO THE WINDOW
ProjectsListViewController *projects = [[ProjectsListViewController alloc] init];
navigationController = [[UINavigationController alloc] initWithRootViewController:projects];
[window setRootViewController:navigationController];
[window makeKeyAndVisible];
return YES;
}
ProjectsViewController
-(IBAction)loadDetails
{
ProjectNavigationController *projectNavPanel = [[ProjectNavigationController alloc] init];
ProjectDetailController *projectDetailPanel = [[ProjectDetailController alloc] init];
ProjectSplitViewController *splitRootController = [[ProjectSplitViewController alloc] init];
[splitRootController setViewControllers:[NSArray arrayWithObjects:projectNavPanel, projectDetailPanel, nil]];
[[self view] removeFromSuperview];
[[appDelegate window] setRootViewController:splitRootController];
}
UISplitViewController Naviagaion
- (void)loadProjects
{
// LOAD THE PROJECTS LIST BACK INTO VIEW
ProjectsListViewController *projectsList = [[ProjectsListViewController alloc] init];
[[[self parentViewController] view] removeFromSuperview];
[[appDelegate window] setRootViewController:projectsList];
}
Now, I know this is wrong, and unsurprisingly it is having adverse effects on other methods. In fact, as I type this, I noticed that the Projects page is being loaded in a navigationController on launch, but placed directly on the window when the splitViewController is closed. Can anyone help me by explaining the correct method of achieving this?
Thanks
Apple say that a UISplitViewController must be the topmost view controller in your app, and that it must be there for the entire lifetime of your app. As you've noticed, if you ignore this, everything can break.
There are some alternatives out there that don't break this way, e.g. MGSplitViewController. Google around. If you have the time, you could even cook your own implementation of a split view controller and be in complete control.
If you really want to use Apple's UIsplitViewController in "crazy" ways, then you can install it as root VC (as Apple demand), and have it at the root all the time, but then show other UIs modally over the top of it. Then hide the modal UI to make the split view controller appear. This is nasty and hacky though.
A while back I asked a related question that may be of interest:
Best way to switch between UISplitViewController and other view controllers?
I don't know if what I'm trying to do is possible, but because I haven't the desired results, I guess not.
What I'm trying and need to do is to call a SplitViewController from a previous ViewController, using presentViewController.
I know, SplitViewController have to be the rootViewController, but I need to explore the most possible options to achieve what I need to do.
I have a MainMenu with buttons, and with every button, I need to call a SplitViewController. First, how can do this?
What I'm trying to do is this:
First, in AppDelegate I'm calling the MainMenu, and add as a subview and other things:
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:self.mainMenu.view];
[self.mainMenu presentModalViewController:self.firstMenu animated:NO];
[self.window makeKeyAndVisible];
return YES;
}
Then, in the MainMenu, I'm calling SecondViewController, in modal view, using this code:
SecondViewController *secV = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
secV.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:secV animated:YES];
In this SecondViewController, I'm creating SplitViewController, with Master & DetailViewController's, using this code:
-(void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UISplitViewController *splitViewController = [[UISplitViewController alloc]init];
SecondMenuViewController *secMenu = [[SecondMenuViewController alloc]init];
UINavigationController *navLef = [[UINavigationController alloc]init];
[navLef pushViewController:secMenu animated:NO];
SecondMainViewController *secMain = [[SecondMainViewController alloc]init];
UINavigationController *navRig = [[UINavigationController alloc]init];
[navRig pushViewController:secMain animated:NO];
splitViewController.delegate = secMain;
splitViewController.viewControllers = [NSArray arrayWithObjects:navLef, navRig, nil];
MainAppDelegate *mainApp = [[MainAppDelegate alloc]init];
[mainApp changeRootViewController:splitViewController];
navRig = nil;
navLef = nil;
secMain = nil;
secMenu = nil;
splitViewController = nil;
}
As you can see, I'm calling a method in MainAppDelegate, to change view and RootViewController, because SplitViewController have to be RootViewController. This is the method:
-(void)changeRootViewController:(UISplitViewController *)splitViewController{
[self.window addSubview:splitViewController.view];
self.window.rootViewController = splitViewController;
}
I know, this looks like a mess. And when I run, the SplitViewController never shows, so I assume, what I'm trying to do is not possible? Or In what I'm wrong?
If it is everything, what can I do to show a SplitViewController after my MainViewController?
I'm using XCode4.4 and iOS5
Thank you very much
A better way would be to make your UISplitViewController the root view controller in application:didFinishLaunchingWithOptions:. Then present your MainMenu on top of it. You can change the subviews displayed by the split view controller to correspond to what button the user pushes in your MainMenu.
First, didFinishLaunchingWithOptions: is too early to be calling presentModalViewController. You haven't even got an interface yet!
Second, you don't seem to have a root view controller (although perhaps you're getting one from a nib? you should probably stop doing that; use the techniques shown in the current application templates).
Third, note that now that we have custom container views, there is no need for you to use UISplitViewController at all; you can construct your own view / view controller hierarchy, and you might be happier doing so, since UISplitViewController is not a very well-constructed class.
I've created an almost-empty UIViewController class named MyViewController. In the viewDidLoad I'm setting the title and adding a close-button to the navigationItem.leftBarButtonItem.
I'm presenting my MyViewController like this:
MyViewController *myViewController = [[MyViewController alloc] init];
UINavigationController *nc = [[UINavigationController alloc] myViewController];
nc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:nc animated:YES];
When the viewController is presented, the background of it's view is just black. How can I setup it's view to fill-out the screen with an empty view -- just like when the UIViewController is setup in a Storyboard?
I've tried adding the following to the viewDidLoad, but the view is still black:
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
By default a VC's view gets created in -loadView which you usually neither call, nor override. It gets called automatically the first time you request the the VC's view property.
The view's size is automatically set to the 'empty space', like everything except for the status bar without a NavigationController, minus the navbar when using one etc. You shouldn't worry about its size - usually it's just fine.
You can add your own views in -viewDidLoad and remove them again (for low-memory reasons) in -viewDidUnload.
You can add an UIImageView with custom image and then sent it to back
So in your viewcontrollers viewDidLoad
UIImageView *back = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"MyBG"]];
[self.view addSubview:back];
[self.view sendSubViewToBack:back];
If you are using a XIB file you should init the view controller through the method initWithNibName: bundle:
Supposed you've got:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view.backgroundColor = [UIColor whiteColor];
[self.window setRootViewController:rootViewController];
[self.window makeKeyAndVisible];
UINavigationController *modal = [[UINavigationController alloc] initWithRootViewController:[[PTFrontViewController alloc] init]];
modal.modalPresentationStyle = UIModalPresentationFormSheet;
[rootViewController presentViewController:modal animated:YES completion:NULL];
return YES;
}
whereas PTFrontViewController and PTBackViewController view controllers have nothing interesting for sake of this example.
How could you push an instance of PTBackViewController from PTFrontViewController animating as in UIViewAnimationTransitionFlipFromLeft or UIViewAnimationTransitionFlipFromRight?
I am already well aware of these three things:
this is not exactly how you should make use of presentViewController
there is a good reason for UINavigationController's default animation
there are several answers how to "customize" UINavigationController's default animation while pushing and poping, but if you try the code for your self you will notice that when a view controller is presented via presentViewController there are drop shadows and background views that won't get animated correctly
So please answer taking these things in mind. Thank you.
First - forget UINavigationController. If you don't need the default animation, just put a UINavigationBar into your controllers. It will get a little easier.
Second - this is a difficult problem, you can't create such an animation only within the modal controller because the background wouldn't be repainted.
Sincerely, the easist solution I see is too forget the modal controller and just add the view controller as a child of your root controller. Then you can control all the animations but you have to write everything by yourself (including the background fading).
I want to place two views side by side like they did in the MAIL app for the ipad. Is this possible with UINavigationController?
I would like to place two UINavigationController side by side
Dont worry about the syntax I just want to know if it is possible
UINavigationConroller *left;
UINavigationController *right;
[WIndow addSubView: left];
[WIndow addSubView:right];
UIWindow inherits directly from UIView, so it is possible to add multiple objects to it, though if you add multiple view controllers then only one of them will receive rotation events.
The correct means to do a Mail-type presentation is to use a UISplitViewController, which automatically handles the transition between two view and one view mode. The Xcode template for a 'Split View-based Application' will even set you up so that you have an button to view the left tableview as a UIPopoverController if you're in portrait.
EDIT: example code, with a split view controller:
// a tiny little method to vend a new navigation controller; following Cocoa patterns, because it
// has 'new' in the name it vends an owning reference (ie, not autoreleased, retain count +1)
- (UINavigationController *)newController
{
UITabBarController *tabBar = [[UITabBarController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tabBar];
[tabBar release];
return navController;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// generate a split view controller
UISplitViewController *splitView = [[UISplitViewController alloc] init];
// grab a couple of navigation controllers
UINavigationController *navController1 = [self newController];
UINavigationController *navController2 = [self newController];
// add the navigation controllers to the split view controller
splitView.viewControllers = [NSArray arrayWithObjects:navController1, navController2, nil];
[navController1 release];
[navController2 release];
// and put the whole thing on screen
[window addSubview:splitView.view];
[window makeKeyAndVisible];
return YES;
}