I was hoping that you could help me with a small issue.
I have a program that has a tab bar controller with 5 tab bar items. On the main page this tab bar appears and I can switch between the 5 tab bar items with no issues.
On some views I have a button which loads a xib file, some of which are xibs that are selectable from the tab bar. However, whenever I load a view using a button I lose the tab bar - and this is my issue.
My current way of loading between views is as follows:
- (IBAction)newGamePressed
{
NewGameIntro *screen = [[NewGameIntro alloc] initWithNibName:nil bundle:nil];
screen.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:screen animated:YES];
}
This is probably quite an obvious solution but I am pretty new to Objective-C and searching the forum I couldn't find anything particularly obvious.
Any help that anyone can give would be much appreciated.
Thanks in advance!
PS The way I loaded the TabBarController was as follows (if this helps) in the delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[[NSBundle mainBundle] loadNibNamed:#"TabBarController" owner:self options:nil];
[self.window addSubview:rootController.view];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
You are presenting a modal view which will cover the entire window as it is designed to. If your goal is to simply push a new controller onto the stack you will need your tab's root views to be a UINavigationController upon which you will simply push another view using
[self.navigationController pushViewController:...]
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 have a Tab Bar application that shows several tabs. I case of notification I want user to be directed to a view controller with the ability to push "Back".
When my app was only Navigation controller app, I used this code in my AppDelegate:
UIStoryboard *mainStoryboard = self.window.rootViewController.storyboard;
DetailViewController *detailViewController = (DetailViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"detailViewController"];
[(UINavigationController*)self.window.rootViewController pushViewController:detailViewController animated:NO];
This code used to work fine until I converted my app to be Tab Bar. Seems like the "self.window.rootViewController" is not longer of type "UINavigationController" and therefore calling "pushViewController" generates an exception saying method doesn't exist.
Any way to make this work on a Tab Bar application?
Thanks
You should present your notification controller as modal (presentViewController:animated:completion:), and inside that controller you provide a way to close it, probably a button on top bar.
EDIT:
Thanks for Dimitry's answer I was able to go on the right track. I just had to make a small trick in order to preserve the top navigation bar and the bottom tool bar. I did it by using a "UINavigationController" instance. Here is the code:
UIStoryboard *mainStoryboard = self.window.rootViewController.storyboard;
DetailViewController *detailViewController = (DetailViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"detailViewController"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
navigationController.navigationBar.tintColor = [UIColor blackColor];
navigationController.toolbar.tintColor = [UIColor blackColor];
[self.window.rootViewController presentViewController:navigationController animated:YES completion:NULL];
I seem to be having an issue since I started using iOS 6, which doesn't appear when using iOS 5. At first, I thought it might just be a simulator bug, but since testing it on my iPhone 5 today, I can see that it's not just in the simulator.
I'm creating everything programmatically — I seem to prefer doing it that way (I assume it's because of my HTML/CSS background!) — but I'm still reasonably new to Objective-C, and I couldn't find a full example of how to set up a navigation controller/table view programmatically, so I put it together from the nuggets of information I could find, and therefore, I could be doing something fundamentally wrong. However, it's worked (and still works) perfectly on iOS 5.
The problem is that I have a black bar between the navigation bar and the table view, but the strange thing is that if I push a view and go back to that original view, the bar disappears, and doesn't reappear until I completely restart the app.
The following image is of the app at launch (1), as I'm pushing a view in (2), and the initial view, after I've gone back to it (3):
This is what I have as my code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController *rootController = [[RootViewController alloc] init];
self.window.rootViewController = rootController;
self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootController];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
NSLog(#"Window frame: %#", NSStringFromCGRect(self.window.frame));
return YES;
}
RootViewController.m
- (void)loadView
{
self.title = #"Title";
self.tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] bounds] style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.view = self.tableView;
NSLog(#"tableView frame: %#", NSStringFromCGRect(self.tableView.frame));
UIBarButtonItem *newViewButton = [[UIBarButtonItem alloc] initWithTitle:#"New View"
style:UIBarButtonItemStylePlain
target:self
action:#selector(newViewButtonTapped:)];
[self.navigationItem setRightBarButtonItem:newViewButton animated:NO];
}
I added the NSLogs to see if they showed anything that might help me. The output is:
tableView frame: {{0, 0}, {320, 480}}
Window frame: {{0, 0}, {320, 480}}
Can anyone give me any ideas about what I'm doing wrong? It seems is having a similar/the same problem (Black bar overtop navigation bar — in the comments), but I haven't found an answer.
Thanks, in advance!
You're adding the RootViewController to the window twice, once by setting UIWindow.rootViewController (which internally does [window addSubview:rootViewController.view]) and again by adding it as a subview of the navigation controller.
You should be doing this:
RootViewController *rootController = [[RootViewController alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootController];
self.window.rootViewController = navigationController;
As a rule of thumb, never add a view directly to the window unless you know that you actually want to.
In my app delegate, I have:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[CGMContainerViewController alloc] init];
self.window.rootViewController = self.viewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
I have an unmodified CGMContainerViewController.h/m and a modified CGMContainerViewController.xib (added a button). The view is linked to the File Owner.
Why is the view from the XIB not showing?
EDIT: Is it because I'm trying to load a view with a status bar into the window?
From the looks of your code, you aren't loading any XIB files. You need to call initWithNibName:
self.viewController = [[CGMContainerViewController alloc] initWithNibName:
#"CGMContainerViewController" bundle: nil];
Make sure that your File's Owner in the XIB is set to CGMContainerViewController
Okay. I think it was because for a UINavigationController, you have to set the rootViewController and all the layers. I just changed it to a UIViewController because I didn't really need the navigation, just the navigation bar.
The problem which I faced is .If you name your viewController with special character with "-" and some other things your xib will not get loaded
example--> (wrong naming)
view-controller1 (If naming is like this then the xib wont get loaded)
example--->(correct naming)
viewController1 (xib will get loaded)
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).