When a user clicks on a button, I create a UITabBarController with two viewcontrollers attached to it.
I then push the TabBarController onto my navigationController stack and it displays without problems. The problem is trying to set a badge on one of the tab bar items when the tabbarControlelr is loaded, I tried:
[[self.tabBarController.viewControllers objectAtIndex:1] setBadgeValue:#"1"];
and a whole lot of variations of this one, but none gives me a round red button thingy on the tabbar item.
Any suggestions?
Thanks,
Ron
EDIT
Code how I present the tabBarController
Airline_RosterAppDelegate *appDelegate = (Airline_RosterAppDelegate *)[[UIApplication sharedApplication] delegate];
CrewHere *vc = [[CrewHere alloc] initWithNibName:#"CrewHere" bundle:nil];
vc.title = #"Crewlist";
MessagesDetailed *mvc = [[MessagesDetailed alloc] initWithNibName:#"MessagesDetailed" bundle:nil];
mvc.title = #"Messageboard";
[tabbar setViewControllers:[NSArray arrayWithObjects:vc, mvc, nil]];
[tabbar setToolbarItems:[NSArray arrayWithObjects:#"Crewlist", #"Messageboard", nil]];
[appDelegate.navigationController pushViewController:tabbar animated:YES];
The property 'badgeValue' is defined on a UITabBarItem. You would set it with:
UITabBarItem *item = [self.tabBarController.tabBar.items objectAtIndex: 1];
item.badgeValue = #"1";
...
The above won't work, nor will your code, when 'the UITabBarController is loaded.' You can only do it after the controller appears because only at that point can you be sure that all the view elements actually exist. Put it in viewDidAppear:animated: for the tabBarController
[Edit, after your Edit]
You can't call setToolbarItems: with an NSArray of NSString; the array needs UITabBarItem. Instantiate with - (id)initWithTitle:(NSString *)title image:(UIImage *)image tag:(NSInteger)tag
Also, it appears that you are mixing tool bar and tab bar - they are different.
I am an iOS development newbie. I want to go to another page (CountryViewController) from my current page (CityViewController) on the click of a button. How would I do that? Pardon me if this is a very beginner question.
There are several ways. I assume you are using a UINavigationController. If so then you can create the VC and do this inside of your parent view controller.
[self.navigationController pushViewController:viewController animated:YES]
So basically you are trying to build a multi-view app. There are many ways to do so. Bu I'll list 3 common ones -
[self.view insertSubview:newViewController.view atIndex:3];
Using UINavigationController
Finally using modalViewController - [self presentModalViewController:newViewController animated:YES];
In second method, I use this controller without UINavigationTabBar. Hide this navigationBar & provide custom buttons based on which [self.navigationController popViewControllerAnimated] should occur.
ViewController2 *newView = [self.storyboard instantiateViewControllerWithIdentifier:#"viewController"];
[self.navigationController pushViewController:newView animated:YES];
set storyboard id in ViewController2 "Identity inspector".
I have came across the same problem. While User logged in once need to redirect to different page or else need to stay in homepage by default.
Here is the code snippet.
Here
N_loginmsg = #"success";
NSString *N_loginmsg = [[NSUserDefaults standardUserDefaults]objectForKey:#"remember_loginmsg"];
NSString *storyboardId;
if (N_loginmsg != nil && [N_loginmsg isEqual:#"Success"])
{
storyboardId = #"ListViewController";
}
else
{
storyboardId = #"HomeViewController";
}
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UIViewController *initViewController = [storyboard instantiateViewControllerWithIdentifier:storyboardId];
UINavigationController *mynav = [[UINavigationController alloc]initWithRootViewController:initViewController];
self.window.rootViewController = mynav;
[self.window makeKeyAndVisible];
I've been stuck trying to puzzle this out for a couple days now, and I'll admit I need help.
The root view controller of my application is a tab bar controller. I want to have each tab bar a different navigation controller. These navigation controllers have completely different behavior.
So how do I set this up in terms of classes? Per Apple's documentation, I'm not supposed to subclass UINavigationViewController. So where do I put the code that drives each of these navigation controllers? Does it all get thrown in App Delegate? That would create an impossible mess.
This app should run on iOS 4.0 or later. (Realistically, I can probably require iOS 4.2.)
This is taken from one of my applications. As you say, you are not supposed to subclass UINavigationController, instead you use them as they are and you add viewcontroller on the UINavigationController's. Then after setting the root viewcontroller in each UINavigationController, you add the UINavigationController to the UITabBarController (phew!).
So each tab will "point" to a UINavigationController which has a regular viewcontroller as root viewcontroller, and it is the root viewcontroller (the one you add) that will be shown when a tab is pressed with a (optional) navigationbar at top.
UITabBarController *tvc = [[UITabBarController alloc] init];
self.tabBarController = tvc;
[tvc release];
// Instantiates three view-controllers which will be attached to the tabbar.
// Each view-controller is attached as rootviewcontroller in a navigationcontroller.
MainScreenViewController *vc1 = [[MainScreenViewController alloc] init];
PracticalMainViewController *vc2 = [[PracticalMainViewController alloc] init];
ExerciseViewController *vc3 = [[ExerciseViewController alloc] init];
UINavigationController *nvc1 = [[UINavigationController alloc] initWithRootViewController:vc1];
UINavigationController *nvc2 = [[UINavigationController alloc] initWithRootViewController:vc2];
UINavigationController *nvc3 = [[UINavigationController alloc] initWithRootViewController:vc3];
[vc1 release];
[vc2 release];
[vc3 release];
nvc1.navigationBar.barStyle = UIBarStyleBlack;
nvc2.navigationBar.barStyle = UIBarStyleBlack;
nvc3.navigationBar.barStyle = UIBarStyleBlack;
NSArray *controllers = [[NSArray alloc] initWithObjects:nvc1, nvc2, nvc3, nil];
[nvc1 release];
[nvc2 release];
[nvc3 release];
self.tabBarController.viewControllers = controllers;
[controllers release];
This is how I go from one viewcontroller to another one (this is done by tapping a cell in a tableview but as you see the pushViewController method can be used wherever you want).
(this is taken from another part of the app)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.detailedAnswerViewController == nil) {
TestAnsweredViewController *vc = [[TestAnsweredViewController alloc] init];
self.detailedAnswerViewController = vc;
[vc release];
}
[self.navigationController pushViewController:self.detailedAnswerViewController animated:YES];
}
The self.navigationcontroller property is of course set on each viewcontroller which are pushed on the UINavigationController hierachy.
I have 2 XIBs with their corresponding View Controllers. Will call them MainViewController and AboutViewController. The names of the XIBs are exactly the same as the names of the ViewControllers. Upon a button click in the MainViewController, I want to show the AboutViewController.
I have code like this in the MainViewController:
- (IBAction) infoButtonAction:(id)sender {
AboutViewController *aboutViewController = [[AboutViewController alloc] init];
[[self navigationController] pushViewController:aboutViewController animated:YES];
}
But nothing happens, the AboutViewController does not show up. I also tried this line:
AboutViewController *aboutViewController = [[AboutViewController alloc] initWithNibName:#"AboutViewController" bundle:nil];
But to no avail. What am I missing?
Examine your AboutViewController.xib in Interface Builder and ensure that the File Owner is set to AboutViewController in the class inspector. Also ensure that the view property of the File Owner is wired into the View in the xib. Finally, the code to display it should read:
- (IBAction) infoButtonAction:(id)sender {
AboutViewController *aboutViewController = [[AboutViewController alloc] initWithNibName:#"AboutViewController" bundle:nil];
[[self navigationController] pushViewController:aboutViewController animated:YES];
[aboutViewController release];
}
Also, you should check that this is non-nil inside that same method:
UINavigationController * nc = self.navigationController;
do you get an error? Did you check that the button is correctly wired in IB?
did you try using a modal view controller?
I have an application where I need to remove one view from the stack of a UINavigationController and replace it with another. The situation is that the first view creates an editable item and then replaces itself with an editor for the item. When I do the obvious solution within the first view:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];
I get very strange behavior. Usually the editor view will appear, but if I try to use the back button on the nav bar I get extra screens, some blank, and some just screwed up. The title becomes random too. It is like the nav stack is completely hosed.
What would be a better approach to this problem?
Thanks,
Matt
I've discovered you don't need to manually mess with the viewControllers property at all. Basically there are 2 tricky things about this.
self.navigationController will return nil if self is not currently on the navigation controller's stack. So save it to a local variable before you lose access to it.
You must retain (and properly release) self or the object who owns the method you are in will be deallocated, causing strangeness.
Once you do that prep, then just pop and push as normal. This code will instantly replace the top controller with another.
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];
// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];
In that last line if you change the animated to YES, then the new screen will actually animate in and the controller you just popped will animate out. Looks pretty nice!
The following approach seems nicer to me, and also works well with ARC:
UIViewController *newVC = [[UIViewController alloc] init];
// Replace the current view controller
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
[viewControllers removeLastObject];
[viewControllers addObject:newVC];
[[self navigationController] setViewControllers:viewControllers animated:YES];
From experience, you're going to have to fiddle with the UINavigationController's viewControllers property directly. Something like this should work:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[[self retain] autorelease];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
self.navigationController.viewControllers = controllers;
[self.navigationController pushViewController:mevc animated: YES];
Note: I changed the retain/release to a retain/autorelease as that's just generally more robust - if an exception occurs between the retain/release you'll leak self, but autorelease takes care of that.
After much effort (and tweaking the code from Kevin), I finally figured out how to do this in the view controller that is being popped from the stack. The problem that I was having was that self.navigationController was returning nil after I removed the last object from the controllers array. I think it was due to this line in the documentation for
UIViewController on the instance method navigationController
"Only returns a navigation controller if the view controller is in its stack."
I think that once the current view controller is removed from the stack, its navigationController method will return nil.
Here is the adjusted code that works:
UINavigationController *navController = self.navigationController;
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
navController.viewControllers = controllers;
[navController pushViewController:mevc animated: YES];
Thanks, this was exactly what I needed. I also put this in an animation to get the page curl:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
UINavigationController *navController = self.navigationController;
[[self retain] autorelease];
[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7];
[UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO];
[navController popViewControllerAnimated:NO];
[navController pushViewController:mevc animated:NO];
[UIView commitAnimations];
0.6 duration is fast, good for 3GS and newer, 0.8 is still a bit too fast for 3G..
Johan
If you want to show any other view controller by popToRootViewController then you need to do following:
UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:#"WelcomeScreenVC" bundle:[NSBundle mainBundle]];
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
[viewControllers removeAllObjects];
[viewControllers addObject:newVC];
[[self navigationController] setViewControllers:viewControllers animated:NO];
Now, all your previous stack will be removed and new stack will be created with your required rootViewController.
I had to do a similar thing recently and based my solution on Michaels answer. In my case I had to remove two View Controllers from the Navigation Stack and then add a new View Controller on. Calling [controllers removeLastObject]; twice, worked fine in my case.
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];
searchViewController = [[SearchViewController alloc] init];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
// In my case I want to go up two, then push one..
[controllers removeLastObject];
navController.viewControllers = controllers;
NSLog(#"controllers: %#",controllers);
controllers = nil;
[navController pushViewController:searchViewController animated: NO];
This UINavigationController instance method might work...
Pops view controllers until the specified view controller is the top view controller and then updates the display.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
Here is another approach that doesn't require directly messing with the viewControllers array. Check if the controller has been pop'd yet, if so push it.
TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil];
if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound)
{
[navigationController pushViewController:taskViewController animated:animated];
}
else
{
[navigationController popToViewController:taskViewController animated:animated];
}
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
for(int i=0;i<controllers.count;i++){
[controllers removeLastObject];
}
self.navigationController.viewControllers = controllers;
My favorite way to do it is with a category on UINavigationController. The following should work:
UINavigationController+Helpers.h
#import
#interface UINavigationController (Helpers)
- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller;
#end
UINavigationController+Helpers.m
#import "UINavigationController+Helpers.h"
#implementation UINavigationController (Helpers)
- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller {
UIViewController* topController = self.viewControllers.lastObject;
[[topController retain] autorelease];
UIViewController* poppedViewController = [self popViewControllerAnimated:NO];
[self pushViewController:controller animated:NO];
return poppedViewController;
}
#end
Then from your view controller, you can replace the top view with a new by like this:
[self.navigationController replaceTopViewControllerWithViewController: newController];
You can check with navigation view controllers array which you give you all view controllers that you have added in navigation stack. By using that array you can back navigate to specific view controller.
For monotouch / xamarin IOS:
inside UISplitViewController class;
UINavigationController mainNav = this._navController;
//List<UIViewController> controllers = mainNav.ViewControllers.ToList();
mainNav.ViewControllers = new UIViewController[] { };
mainNav.PushViewController(detail, true);//to have the animation
Alternatively,
You can use category to avoid self.navigationController to be nil after popViewControllerAnimated
just pop and push, it's easy to understand, don't need to access viewControllers....
// UINavigationController+Helper.h
#interface UINavigationController (Helper)
- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated;
#end
// UINavigationController+Helper.m
#implementation UINavigationController (Helper)
- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIViewController *v =[self popViewControllerAnimated:NO];
[self pushViewController:viewController animated:animated];
return v;
}
#end
In your ViewController
// #import "UINavigationController+Helper.h"
// invoke in your code
UIViewController *v= [[MyNewViewController alloc] init];
[self.navigationController popThenPushViewController:v animated:YES];
RELEASE_SAFELY(v);
Not exactly the answer but might be of help in some scenarios (mine for example):
If you need to pop viewcontroller C and go to B (out of stack) instead of A (the one bellow C), it's possible to push B before C, and have all 3 on the stack. By keeping the B push invisible, and by choosing whether to pop only C or C and B altogether, you can achieve the same effect.
initial problem
A -> C (I want to pop C and show B, out of stack)
possible solution
A -> B (pushed invisible) -> C (when I pop C, I choose to show B or also pop it)
I use this solution to keep the animation.
[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];