I have an app (with a UITabBarController) that can reach the final payment view via two tabs, each with their own NavigationController.
(The illustration only shows the two tabs in question)
When payment is successful I want to reset both NavigationControllers and present tab 1.
If I was on tab 1 I could just use this code:
self.navigationController?.popToRootViewControllerAnimated(true)
But if I was on tab 2 that only brings me to the first viewController in navigationController in tab 2...
How can I present another tab while also resetting the NavigationController I am on (and others)?
Any help is very much appreciated. Thank you !
1) Create a custom UITabBarController or make a category that has a method called something like: completePayment()
- (void) completePayment
{
[self setSelectedIndex:n] // n is the index of your destination tab
[[[self viewControllers] objectAtIndex:m] popToRootViewControllerAnimated: YES]; // reset the stack in the current tab
}
2) trigger this method from the ViewController that the paymant is done as such:[self.tabbarController completePayment];
3) Profit!
There are probably ways of doing this in somewhat hacky ways, but what I will suggest you is that you reconsider your flow when you ask yourself a question like this one. Each UITab is designed to hold a UINavigationView that is self contained and independent from other UITab elements. If you force reset a tab that you are not on, you are breaking the flow and confusing the users.
So I recommend you push your Payment ViewController, wherever you need it in the app. And don't mess with other Tabs states.
Related
As someone who usually used separate xibs in the past I thought I'd give storyboard a go as it seemed a lot simpler to use and much easier to develop with. I've been writing an application where the essential set up is this:
At the top of all this is a UINavigationController (first level). Then I have Multiple UIViewControllers (second level) with buttons in them which you can tap to switch between the second level UIViewControllers.
However a problem occurs when I start switching between the second level UIViewControllers. I first thought this was an initialisation problem with the NSMutableArrays because in my code I have a NSTimer set to loop periodically and found when I set a breakpoint during it, when I went forward to the next timer tick event there appeared to be different instances of the same NSMutableArrays and it seemed a gamble to try and insert new values into these array with it sometimes working, sometimes not (as it may or may not insert into the correct instance).
Then, looking at the memory usage under Debug Navigator I found the issue. Each time I "switched" between the UIViewControllers a new UIViewController was being initiated, along with all new variables.
The code I am using to switch between them is
-(void) perform {
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
Or essentially a push segue transition. This also explains why when I tried to switch back to my view, the data on that view was lost as it is a complete new view.
Does anyone know how to switch between multiple ones of these UIViewControllers (say 5) essentially like a UITabViewController would except without the tab bar being present?
First option you can do this: You can use a tabbarcontroller for switching viewcontroller and hidden the tabbar. Then on buttonclick setthe tabbar index.
Second option you can do this: Create one more view controller and in this viewcontroller subview the all switching viewController and when you want to switch viewcontroller just bring that viewcontroller view to front by delegate.
Do you need the navigation bar and other features provided by your top level navigation controller?
If not, you could use a UIPageViewController instead.
You set up all your second level view controllers and then just have to tell the page view controller which one to display.
If you implement the associated delegate methods, it will automatically provide swipe gestures to switch between them and nice animations to get them on and off screen.
You can also get it to put a UIPageControl at the bottom showing a dot for each VC with the dot for the current VC highlighted.
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 screen (My Profile) which can be accessed from two paths:
Login -> Content -> Profile
Login -> Register -> Profile.
In both paths, view's are shown with:
[self.navigationController pushViewController...];
But my problem is, after the user registers and completes his profile, it should go "Back" to the Content view. (Obviously it won't work with navigation controller stack, since Content isn't in the navigation controller).
My question is, what suggestions do you have?
PS: I know this isn't an actual question, but I've been thinking about this for a few hours now and I didn't come up with anything. Maybe some of you have had to deal with similar cases.
Edit: Basically the question can be generalised to:
How do you deal with a circular application flow?
Edit: I've solved this by pushing from Register to Content and then Profile in viewWillAppeare without animation (so what I need is in the stack), but I'm still interested in dealing with circular application flows.
My suggestion would by to change [UINavigationController viewControllers].
So after you end your registration you can do something like
ContentVC *content = [[ContentVC alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:content]];
[self.navigationController popToRootViewControllerAnimated:YES];
Push Contentview controller after profile complated in second case , while in first case you have already pushing it.
1)Make your Login viewController the rootController of the a UINavigationController.
2)From Login you can Push Content ViewController
3)From Content VC you can push to Profile VC.
Now if you want to get back to Content from profile do this:
[self.navigationController popViewControllerAnimated:YES];
And if you want o get back from Profile directly to Login do this:
[self.navigationController popToRootViewControllerAnimated:YES];
If you do not want the navigation bar you can hide the bar.
Update
Take a different approach, other than the navigation controller stack:
1) Make a controller class with a 2 functions:
-(void)loadViewControllerWithIndex:(int)index;
and
-(void)unLoadCurentViewController;
2) You can call these functions and load and unload the view controllers from a this controller class.
3) So you initially load this class and import file in this class of other view controllers. You can take a UIViewController object topViewController, so that you can you can keep a track on which controller is currently displayed and it will help when you want to unload a controller.
4) In load controller with Index function you can add the controller view and in unload you may remove them.
I have a view in which a user selects an action to take and on that next screen there is a save and a back button. For both of the buttons the last line is dismissViewControllerAnimated:.
I need a way to make the 1st screen show only if the back button is used. save should send back to the main screen/rootViewController I am fairly new to iOS but not programming in general and just need a nudge in the right direction.
Could I set a bool flag to show or not? Maybe I can set the Tag on the view and then check that in the other screens on save/back? I assume I can check the parent view.
Sorry if this is a dup but I cant find anything specifically for this.
EDIT: I am not using a nav controller and am showing the views modally.
The answer will vary depending on how your UIViewControllers are structured and setup. If you're using a uinavigationcontroller then you can POP to the root view controller using:
[self.navigationController popViewControllerAnimated:YES];
If you're presenting your UIViewControllers modally, you can try to dismiss the presenting View Controllers of your modal view controller using the presentingViewController property:
[[[self presentingViewController] presentingViewController] dismissViewControllerAnimated:YES completion:nil];
You may also want to take a look at Unwind Segues if you're using a Storyboard:
What are Unwind segues for and how do you use them?
Finally, as far as determining whether the back button is pressed or another button - that depends on how the app is setup. You'll need to use your own logic (probably if / then statements or case / switch) to determine which button was pressed. You also may want to check out the sender argument in IBActions.
John, to have a UINavigationViewController return to it's root viewcontroller, you use:
[nameOfNavController popToRootViewControllerAnimated:YES];
The other guys are correct that the information you've provided is definitely not enough to determine exactly what you need to do.
You can use the presentingViewController property of a modal view controller to access it's presenting controller.
It turns out that I was using the terminology wrong. I am presenting all views modally and that is the issue, there is no navigation controller. I ended up using NSNotification to build a listener and had the main view controller listen and then dismiss the view and hence show itself. Worked a treat.
here is the link to the code I ended up with.
http://iphonedevsdk.com/discussion/114737/view-heirarchy-issues-possibly-from-the-camera
Hopefully this helps someone else.
I have an app that has the following basic layout, please understand I have done a lot of programming, but I am relatively new to IOS and am yet to wrap my head around the Storyboards/segues properly yet.
Effectively, my app has the following screens:
WelcomeViewConroller ---ModalSeque--> MenuViewController --modalSegue---> newProjectVC || loadprojectVC ---modalSegue-->ProjectScreenVC.
From the project the screen, the user can return to the menuVC screen.
Now, I understand that every segue creates a new instance of a view controller, which is great, I want this to happen, however, when I segue back from my ProjectScreen, and then reenter it again, I get a huge memory leak and very strange behaviour.
I understand that I need to dismiss my View controllers, especially my ProjectScreen when I leave it, however, I can not get this to happen, no matter what I try.
Any help would be greatly appreciated.
In How should I chain viewcontrollers in xcode storyboard? I enumerate a series of ways of going back multiple scenes in a storyboard. But in short, the two easiest options are:
Unwind segues: If only supporting iOS 6 or higher, use unwind segues. So, for example, in your main menu's view controller, implement a unwind segue:
- (IBAction)gotoMainMenu:(UIStoryboardSegue *)segue
{
// if you need to do anything when you return to the main menu, do it here
}
Also make sure to define that in the main menu's .h. Then control+drag from the button that you want the segue to the "exit" button in the panel below the scene, and choose the "gotoMainMenu" option:
Navigation controller: If you need iOS 5 support, then just use a navigation controller and replace the modal segues with push segues. Then you can use popToViewController or popToRootViewControllerAnimated to jump back multiple scenes. If you don't want to show the navigation bar, then select the navigation controller in your storyboard, and in the attributes inspector, uncheck "Shows Navigation Bar":
In this scenario, I actually think it's easiest to make sure your menu scene is the root (and have it do a little detour to the welcome screen, like I discuss in point 4 of that other answer), in which case you can just call popToRootViewController whenever you want to return to the main menu. But, if the main menu is not the root view controller, and you want to pop back to it, you can either pass a point menu controller from scene to scene, or you can have subsequently presented view controllers do something like the following when they want to get back to the main menu:
for (UIViewController *controller in [self.navigationController viewControllers])
{
if ([controller isKindOfClass:[MenuViewController class]])
{
[self.navigationController popToViewController:controller animated:YES];
break;
}
}
Elsewhere on Stack Overflow, you'll see people contemplating ways to nest calls to dismissViewControllerAnimated, or other variations like that. I personally think that navigation controllers and unwind segues are far easier and more elegant.