Subclassing UINavigationController for custom navigation between Views - iphone

I am making a small app with the following view hierarchy with UINavigationController:
Login -> Options -> three different views
The problem is that I would like to navigate between the last 3 views in the following manner:
1<->2
1<->3
2<->3
i.e. to be able to switch to any view from any other view, which reminds UITabViewController functionality. So, it is not hierarchical, it is any-to-any graph. To switch between views I will use buttons in the navigation bar.
The easiest way for me is to subclass UINavigationController, add properties that correspond to my views and implement methods for switching between these views (using pushViewController and popToRootViewController). These methods will be called from the views for switching (navigating).
However the reference says that UINavigationController is not intended for subclassing.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
What do you recommend me to do?

I'll keep the UINavigationController but instead of using the usual pushViewController:, switch views like this:
NSMutableArray *viewControllers = [self.navigationController.viewControllers mutableCopy];
// from here you can modify the order of controllers as much as you want
[viewControllers addObject:nextViewController];
[viewControllers removeObject:self];
[self.navigationController setViewControllers:viewControllers animated:YES];
If you don't want how the animation turns out, you can set animated:NO and either enclose setViewControllers: in an [UIView animate...] block, or add your own custom CAAnimation to the navigation controller's layer.

Use the below code to add a view controller to a navigation controller,
Navigating From first -> second
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
NSMutableArray *navigationarray = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
[navigationarray removeAllObjects];
[navigationarray addObject:secondView];
self.navigationController.viewControllers = navigationarray;
Navigating From first -> third
ThirdViewController *thirdView = [[ThirdViewController alloc] initWithNibName:#"ThirdViewController" bundle:nil];
NSMutableArray *navigationarray = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
[navigationarray removeAllObjects];
[navigationarray addObject:thirdView];
self.navigationController.viewControllers = navigationarray;
The above code will removes all the viewControllers from the Navigation Array and places a fresh View Controller
If u want to go to a particular view controller, then use the below code...
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES]
Change the index to ur view controller in the stack.

Related

Can i take tabbar controller in view controller rather than delegate class?

I am using a UINavigation Controller in a delegate class. After navigating the two views class on the third view class I need a tabbar controller which can control three another ViewControllers and the tabbar should not be seen on first two view controllers. How can i do this ?
- (void)viewDidLoad
{
[super viewDidLoad];
self.title =#"Scan";
tabController =[[UITabBarController alloc]init];
ScanTicketView *scan =[[ScanTicketView alloc]initWithNibName:#"ScanTicketView" bundle:nil];
SearchView *search =[[SearchView alloc]initWithNibName:#"SearchView" bundle:nil];
HistoryView *history =[[HistoryView alloc]initWithNibName:#"HistoryView" bundle:nil];
tabController.viewControllers=[NSArray arrayWithObjects:scan,search,history, nil];
[self presentModalViewController:tabController animated:YES];
}
Follow my answer ... you can add and remove tabBar whenEver you want
link
Yes you can. See this link to an example
In your second view controller where you want to push to you third view(tab controller) do this
UITabBarController *tabBarController=[[UITabBarController alloc]init];
tabBarController.viewControllers=[NSArray arrayWithObjects:firstViewController,secondViewController,thirdViewController, nil];
//[self.navigationController pushViewController:tabBarController animated:YES];// this works too but since it seems to be against Apple's Human interface Guidelines you can present the view instead of pushing
[self presentModalViewController:tabBarController animated:NO];
Ideally TabBarcontroller should be the starting case of an app..but in rare cases where you want to present it in a viewcontroller follow ..
UITabBarController *tabBarController=[[UITabBarController alloc]init];
tabBarController.viewControllers=[NSArray arrayWithObjects:firstViewController,secondViewController,thirdViewController, nil];
[self presentModalViewController:tabBarController animated:NO];

How to pop the split view controller "DetailViewController?"

detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController_iPad" bundle:nil];
[self.splitViewController.splitViewController viewWillDisappear:YES];
detailViewController.strDetailId = [teaserSectionOneArray objectAtIndex:indexValue] ;
NSMutableArray *viewControllerArray = [[NSMutableArray alloc] initWithArray:[[self.splitViewController.viewControllers objectAtIndex:1] viewControllers]];
[viewControllerArray removeAllObjects];
[viewControllerArray addObject:detailViewController];
[[self.splitViewController.viewControllers objectAtIndex:1] setViewControllers:viewControllerArray animated:NO];
[self.splitViewController.splitViewController viewWillAppear:YES];
[viewControllerArray release];
This code using for push to the detail view. How do I pop to another view controller in detail view when I click the button? Its not supported the [self.navigationController popViewControllerAnimated:YES];. How to handle this problem? Thanks in advance!
Wooooow thats NOT how you push a view controller on the stack of a navigationController !!
You don't have to call viewWillDisappear, viewWillAppear and such yourself ! You don't have to add the detailViewController to the splitViewController.viewControllers array yourself either!
What you need to do is this:
Make sure your UISplitViewController has a NavigationController as part of its detailViewController (namely the ViewController that is on the right of the screen should be an UINavigationController, not a regular UIViewController).
The rest is then easy as it works the same way as any regular UINavigationController. The fact that your UINavigationController is the right part of an UISplitViewController is not different than when you use an UINavigationController in any other context.
Simply use pushViewController:animated: and popViewControllerAnimated: methods of UINavigationController to push and pop your view controllers. Access your UINavigationController from your UISplitViewController using either a custom IBOutlet that you added to point to it, or by accessing (UINavigationController*)[self.splitViewController.viewControllers objectAtIndex:1].

Talking to A UITabBarController In Story boards

So In my app, I use a storyboard, and the initial viewController is a UITabBarController. What I want to do is when the app launches, I want to be able to set wheather the tabBarController has 3 items or 4. So in the appDel, I plan to check weather that user should see 3 or 4 tabs, and then the tabBarController should reflect that.
I tried subclassing the tabBarController, but the its not working:
#implementation TabBarController
-(id) init{
if ((self = [super init])) {
[[[[self tabBar] items] objectAtIndex:2] setEnabled:YES];
[[[[self tabBar] items] objectAtIndex:3] setEnabled:NO];
[[[[self tabBar] items] objectAtIndex:3] setHidden:YES];
}
return self;
}
#end
Any help would be apprciated.
Thanks in advance.
I'm thinking the easier (probably not best) way to do this is by having the TabBar controller not be the initial view controller and creating two tab bar controllers. From your initial view you can decide with tab bar controller you will show. You can also have the two tab bar controllers linked to the same tabs/viewcontrollers (the ones that are repeated between them).
In the initial view controller you add some code to know which segue you will perform and voila.
Something like this:
I am looking into a code solution to this though (seems interesting!). Will update if I find anything.
EDIT:
Here's the coded solution. (I am using storyboards but I am sure you can translate the code to work with nib files).
The first thing to do is create an instance of the storyboard:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:[NSBundle mainBundle]];
Then instantiate the tabbarcontroller:
UITabBarController *myTab = [storyboard instantiateViewControllerWithIdentifier:#"tabBar"];
Then instantiate the new view controller you want to add to the tabbar:
UIViewController *newVC = [storyboard instantiateViewControllerWithIdentifier:#"newView"];
This is the interesting part, you put all the view controller/tabs into an array and then add or remove views from that array. Then you set the view controllers of the tabbarcontroller to the modified array (I add one viewController and remove another).
NSMutableArray* controllers = [myTab.viewControllers mutableCopy];
[controllers insertObject:newVC atIndex:0];
[controllers removeObjectAtIndex:2];
[myTab setViewControllers:controllers];
Then you can just push your tab bar controller like this:
[self.navigationController pushViewController:myTab animated:YES];
Here I am using storyboards and ARC, you can modify it for nibs and release the array if you're not using ARC. This was fun to write! hope it helps! cheers!

Return to the first index of UINavigationController?

I'm doing an application which uses a UINavigationController and I'm switching to other UIViewControllers as follows:
if(self.myViewController == nil){
MyViewController *aViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
self.myViewController = aViewController;
[aViewController release];
}
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.myNavController pushViewController:myViewController animated:YES];
I imagine this is creating a pile of UIViewControllers into the UINavigationController, maybe an array of indexs? I would like to know how to turn back without having to be back one by one.
For example, I'm sailing through a few screens and with a button I would like to return at the first index of navigation. I would also like know how to modify indexes, view, erase and anything pertaining to this issue.
Sorry if I have not explained well.
You've asked two questions.
The first is, how do I get back to my first view controller. As #Thomas Clayson and #ender have answered, you want the popToRootViewControllerAnimated: method of your navigationcontroller object for that.
The second is how to move to a particular index in the view controller stack. The answer to that is, you can set the array of viewControllers explicitly. So you can pull out the current listing of view controllers, modify it, and set it back into the navigationController stack. It'll reset the stack and animate you moving to the top item in the stack.
Thusly:
NSMutableArray *controllers = self.navigationController.viewControllers;
[controllers removeObjectAtIndex:[controllers count] - 1]; //or whatever
[self.navigationController setViewControllers:controllers animated:YES];
NSArray *viewControllers = [[self navigationController] viewControllers];
for (int i = 0; i < [viewContrlls count]; i++){
id obj = [viewControllers objectAtIndex:i];
if ([obj isKindOfClass:[yourViewControllername class]]){
[[self navigationController] popToViewController:obj animated:YES];
return;
}
}
Using this you can come back to any specified viewController.
[self.navigationController popToRootViewControllerAnimated:YES];
Will take you back to the very first view controller (root view controller).
Hope this helps
Use this
NSArray *viewContrlls=[[NSArray alloc] initWithArray:[[self navigationController] viewControllers]];
id obj=[viewContrlls objectAtIndex:1];
[[self navigationController] popToViewController:obj animated:YES];
[viewContrlls release];
You should use popToRootViewControllerAnimated: From UINavigationController class reference:
Pops all the view controllers on the stack except the root view
controller and updates the display.
You can return to the first view with
[self.navigationController popToRootViewControllerAnimated:YES];
That being said, you can also remove a particular view controller, or navigate to a specific index in your view controller if you look at the example.
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:navigationController.viewControllers];
// You can now manipulate this array with the methods used for NSMutableArray to find out / perform actions on the navigation stack
[allViewControllers removeObjectIdenticalTo: removedViewController];
// You can remove a specific view controller with this.
navigationController.viewControllers = allViewControllers;

How can I go back to the first view from the third view directly?

When I push cancel button in the third view, I want to go back to the first view directly.
I also want to remove the second view.
How can I do that?
This is the code.
// this part is in the first view.
self.second = [SecondController alloc] init];
[self.view addSubview:second.view];
// this part is in the second view.
ThirdController *thirdController = [[ThirdController alloc] initWithStyle:UITableViewStyleGrouped];
self.navigationController = [UINavigationController alloc] initWithRootViewController:thirdController];
[self.view addSubview:navigationController.view];
// this part is in the third view.
- (void)cancel {
[self.view removeFromSuperview]; // this only goes to the second view.
}
EDIT:
Can I use popToViewController in called contoller? My app crashes.
I thought popToViewController can be used only in calling controller.
And popToViewController is used when it was pushed.
I did add not push.
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:0] animated:YES];
popToViewController:animated: is a UINavigationController method that you use when popping view controllers off the navigation controller stack. It doesn't fit for this scenario.
This user is adding subviews, not pushing them on a navigation controller stack.
As a note, it appears as a matter of design you should be using a navigation controller with the first view as the root controller, then the second pushed on the stack, and the third pushed on the stack. Then all you have to do is [self.navigationController popToRootViewControllerAnimated:YES].
I think this will work if you want to keep your current architecture:
// this part is in the third view.
- (void)cancel {
// remove the second view (self.view.superview) from the first view
[self.view.superview removeFromSuperView];
// can't recall, possibly you still need to remove the third view, but i think removing the superview will do it.
// [self.view removeFromSuperView];
}
If you prefer to try the UINavigationController route, then the easiest path is to create a new project in Xcode and select the type for a Navigation-Based Application or a Master-Detail Application. This will create a UINavigationController in a nib and add it to your window. You can then set the root view controller in Interface Builder to your FirstViewController class.
If you prefer to create the UINavigationController in code, then that is also possible. I show that below, along with the rest of the code you need, regardless of whether you create your UINavigationController in a nib in IB or in code.
I also recommend reading the View Controller Programming Guide for iOS.
In your app delegate or some other code:
-(void)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions [
// I would recommend setting up the UINavigationController and FirstViewController as IBOutlets in your nib, but it can be done in code.
FirstViewController* fvc = [[FirstViewController alloc] initWithNibName:#"FirstView" bundle:nil];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:fvc];
[window addSubView:navController.view];
[window makeKeyAndVisible];
[fvc release];
[navController release];
}
In the first view controller:
SecondViewController* svc = [[SecondViewController alloc] initWithNibName:#"SecondView" bundle:nil];
[self.navigationController pushViewController:svc animated:YES];
[svc release];
In the second view controller:
ThirdViewController* tvc = [[ThirdViewController alloc] initWithNibName:#"ThirdView" bundle:nil];
[self.navigationController pushViewController:tvc animated:YES];
[tvc release];
In the third view controller:
-(void)cancel {
// returns to the first view controller
[self.navigationController popToRootViewControllerAnimated:YES];
}
Use
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
to go back to a specific view controller.
Try this:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
This will pop to the view at index 1. Hope that Helps!
// this part is in the third view.
- (void)cancel {
self.first = [SecondController alloc] init];
[self.view addSubview:second.view];
}
And I think if you have you don't need to be worried about removing beneath view, later these will removed.