Sometimes in my app that I'm developing, I find myself in a position where I just need to navigate back to a specific tab view of my UITabBarController. When I do this I usually just change the tabBarController's selectedIndex and then use presentViewController: on the tabBarController, like so:
UITabBarController * tabControl = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarControl"];
tabControl.selectedIndex = 1;
[self presentViewController:tabControl animated:YES completion:nil];
Will this cause instance problems? As in.. will doing this simply create another instance of the tab bar tab that I have navigated to, seemingly "reloading" that tab bar view?
As the method name in your code suggests, you will be creating a new view controller object every time you are navigating to a specific tab view. You should only keep a single UITabBarController object in your app, probably in AppDelegate or a singleton object, and use that object for navigation.
Related
In my app, I have a UITabBarController that is set as the initial view controller and has the storyboard ID 'TabBarControl'. TabBarControl has 4 tabs, and off of the second tab(index 1) there is a sequence of UIViewController navigations that goes as such, where VC is ViewController..
UITabBarController > UINavigationController('branchesControl') > VC1('branchesView') > UINavigationController > VC2 > UINavigationController > VC3 > UINavigationController > VC4
I have a UIBarButtonItem named 'Confirm' on the navigation bar in VC4. The Confirm button triggers the following IBAction method:
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarControl"];
tabControl.selectedIndex = 1;
[self presentViewController:tabControl animated:YES completion:nil];
}
The goal is to navigate from VC4 back to the original 'branchesView'
The problem is, I've noticed that a manual set of a color of a UIBarButtonItem on VC2's navbar resets to a default white after hitting Confirm and then revisiting the view stack. I believe that I am creating multiple instances.
How can I simply navigate back to branchesView, and then VC2 without creating new instances?
PS I'm not sure if the new instance is TabBarController or or branchesView, I just know that it would seem that I am creating multiple instances somehow with the confirmClicked: method.
Instead of going back to the existing branchesView, you are presenting a new UITabBarController with a whole new set of child view controllers which is almost certainly not what you want to be doing.
Instead, you need to pop back to the already existing view controller that you want to get back to. You can do this either by reference or index, and in your case, by simply popping to the root (provided the view controller you want to get to is indeed at the root).
You can simply do:
[self.navigationController popToRootViewControllerAnimated:YES];
Your question also makes it seem like each view controller in the stack is wrapped in a new navigation controller. if that is the case, you will need to fix that as well. You should not nest UINavigationControllers. Instead, have a single one as the root for each tab and each time you want to push a new UIViewController onto the stack, you call:
[self.navigationController pushViewController:viewControllerToPush animated:YES];
Here is my set up in storyboard.
I'm trying to use this code: (In an IBAction connected to the UIBarButtonItem in the last VC):
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarControl"];
tabControl.selectedIndex = 1;
[self presentViewController:tabControl animated:YES completion:nil];
}
to navigate from the last ViewController in this picture to that very first UINavigationController in the stack. I was informed that UINavigationControllers are not meant to be nested like this, and that only one UINavigationController should be necessary, but when I successfully remove(which I have done at least 5 times) the other three UINavigationControllers from the rest of the stack, I completely lose my UINavigationBars from the regular ViewControllers.
The problem with navigating the way that I currently am with my 'confirmClicked:' method, is that I create another instance of that first UIViewController in the hierarchy.
Should I:
A) Remove all the unnecessary UINavigationControllera in the view hierarchy, then use 'popToRootViewController:' to correctly navigate from the last VC to the first?
B) Try to navigate another way from the last VC to the first VC, keeping the extra UINavigationControllers in the view hierarchy?
Also, if I remove the Navigation Controllers, how will I keep my navigation bars in the UIViewControllers, since they have been disappearing in the past when removing the Navigation Controllers?
Sorry in advance for the many questions but I have been stuck for a while.
OK
First thing
Remove all those navigation controllers. You only need one.
Second
The code in that IBAction is creating a brand new instance of the TabBarController and placing it over the current stack (this is how to get memory problems).
Third
What you probably want is either an unwind segue (possibly) or code something like this...
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.tabBarController setSelectedIndex:1];
}
I have an application that has a login page. It then moves to a navigation controller that has a collection view as its root view controller. When the app starts and there is only one item in the collection, I want to have the navigation controller automatically push to that item and allow the user to use 'back' to view the collection. This is the same behavior that is in 'Notes' from Apple.
The idea is to allow the user to immediately start to use the app and only 'discover' the need for the collection view after using the app for some time.
I am using IB, storyboards, and segues for my view transitions.
If I programmatically have the root view controller do a performSegue in its viewWillLoad: I get an error about causing a transition while a transition is still in process.
If I move the code calling performSegue into the didLoad, then the user sees a double transition.
A navigation controller usually manages the underlying stack itself. That is, you pass it an initial view controller, and then through segues or pushViewController:animated: the stack is altered. However, there is nothing stopping you from manually altering the stack. In prepareForSegue:sender:, you can check the number of items, and if it's 1, do something like this:
UIViewController *singleItemViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SingleItem"];
NSMutableArray *viewControllers = [sender.destinationViewController.viewControllers mutableCopy];
[viewControllers addObject:singleItemViewController];
sender.destinationViewController.viewControllers = viewControllers;
Now instead of starting at the first view controller of the stack, you start at the second one, with a back button to navigate back.
Actually, I found that the following worked the best for me.
if (/* reason to change back list*/) {
UIViewController * rootViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"backViewController"];
NSArray * viewControllers = #[rootViewController, self];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
setViewControllers seems to be the best way to manipulate the stack.
I programmed my app initially for iPhone using a tab bar controller were the view controllers are initialized once and stays persistent - it does not initialize a new instance of the view controller when I tap the tab bar.
on the iPad I am using a different GUI were instead I have one main view that always stays on the screen, and the rest are popovers segueing from the main view.
I want the popovers to stay persistent (only initialize once) what is the best way of archiving this. If I had been using *.xib files I could have initialized the popover´s view controllers in the main view and then sent a copy of them when segueing, and that way only ever have one instance of them. But I am using Storyboards.
You can't use segues if you want your controllers to be persistent, because segues always instantiate new controllers. You can still use the storyboard, but you have to leave the controllers unconnected, and instantiate them in code, and assign them to a strong property. So, something like:
-(void)presentPopover {
if (! self.vc) {
self.vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyController"];
}
// do what you want here to put vc on screen
}
I found a solution and actually it´s easy, just use a UIPopoverController and initialize it with the view controller you want to present. In this way it will not instantiate a new instance each time a popover is requested.
if (!popoverController)
popoverController = [[UIPopoverController alloc]initWithContentViewController:bellViewController];
[popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
popoverController.delegate=self;
I have App with single storyboard, and two view in same story board. First one is controlled by ViewController and second one is controlled by View.(Two different class files, one inherits UIViewController and another UIView). App loads the first story board which is linked to UIViewController.
Now, I want to check some conditions in NSUserDefaults and skip the first view on app load if the condition is true. Skip first view can also be automatically load second view, this what I am trying to accomplish.
I have searched a lot and all of them were about the Navigation Controller, My views/controllers are not navigation controller, also I dnt want to use that because of the automatic navigation bar which I dont want.
Please help! Thanks.
remember to import the secondView,
and in storyboard you have to give the identifier "SecondView" to secondViewController
if ([[NSUserDefaults standardUserDefaults]boolForKey:#"Yes"]) {
SecondViewController *sv = (SecondViewController *)[self.storyboard
istantiateViewControllerWithIdentifier:#"SecondView"];
sv.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:sv animated:YES completion:Nil];
}