I have a presenting view controller that presents a modal like VC, then when the user is done with that modal, I want to transition to another modal, but calling:
// we are done.
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
if (self.showOtherContoller)
{
UIViewController* aVC = [[UIViewController alloc] initWithNibName:#"someNib" bundle:nil];
[self.presentingViewController aVC animated:NO completion:nil];
}
works, but the presenting base view controller shows for about a second, so it looks all flashy.
Basically I want to replace one view controller with another, not temporarily drop to the presenting view controller.
Thanks for any tips.
Your modal transition style is CoverVertical. The first modal slides back down when dismissed, exposing its presentingViewController, then the second modal slides up, hiding the presentingViewController.
You want to change the transition so it appears that the first modal is replaced by the second modal.
Change both view controllers' modal transition styles to CrossDissolve before dismissing the first and presenting the second. Before dismissing the second, change it to CoverVertical, so it slides down to match the first modal's slide up transition.
Related
I have a firstViewController that I display with
[self.navigationController pushViewController:firstViewController animated:true];
Then when a button in this first VC is clicked, I present a second one:
[self presentViewController:secondViewController animated:true completion:nil];
And from this second one I present the third VC as a modal:
[thirdVC modalPresentationStyle];
[thirdVC setModalPresentationStyle:UIModalPresentationOverCurrentContext];
[self presentViewController:thirdVC animated:true completion:nil];
The problem is that I want to display the firstVC when the validate button of the last VC is clicked. So I have to dismiss the third and the second one. I tried this in the validateAction (in the third VC):
[[self parentViewController] dismissViewControllerAnimated:true completion:nil];
[self dismissViewControllerAnimated:true completion:nil];
But the result is that the thirdVC (the modal one) is dismissed, and from the debugger I can see that the firstVC is covered by the secondVC.
How can I dismiss the second and the third VC at the same time in order to return to the first one?
NOTE: I want to push the firstVC after the dismiss of the others because I have to reload the data (in the fristVC viewDidLoad)
From Apple's Documentation:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
In other words, the third viewController asks the second viewController to dismiss its presented viewController, which is the third one. Then the third viewController tries to dismiss itself, causing UIKit to ask the second viewController to dismiss the third one once more.
You will have to go one layer up and ask the first viewController to dismiss its presented view controller.
It is good practice that the third viewController does not know anything about the presentation hierarchy. You can use delegation to tell the first viewController that the task is finished. An exit segue is a very nice alternative if you use storyboards.
You only have to call -dismissViewControllerAnimated:completion: once if you address the first viewController:
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack.
I am having a strange problem that I can't seem to find the cause for.
When attempting to present a modal view controller on a navigation controller the navigation controller is popping all of my view controllers underneath when the modal is dismissed.
So after pushing a few view controllers and presenting a modal on the topViewController, I end up back at the rootViewController when the modal is dismissed.
Anyone had this happen to them lately, I can't seem to find the reasoning for why this is happening?
This answer is for #rshev:
It was actually a user error. Here's what was happening: I had a view controller with a manually added navigationController on top of it (as a subview/child VC). The nav controller then had 3 VCs in its stack. The third (and visible) VC was presenting an image picker controller. When the image picker was dismissed, I momentarily saw my third VC , then it quickly popped back to the 1st, discarding the other two VC's from memory.
So what went wrong? What I didn't realize is that viewDidAppear (and viewWillAppear) was being called on my content view controller (the one with nav controller for its subview). This content VC was actually setting its navigation controller (and adding it as a subview) on viewDidAppear, thus covering up the original nav controller.
To solve it, I just added a static boolean to determine when the first VC FIRST appears, like so:
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
static BOOL firstAppearance = YES;
if (firstAppearance)
{
firstAppearance = NO;
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"NavigationController"];
[navController.view setFrame:self.view.bounds];
[self.view addSubview:navController.view];
[self addChildViewController:navController];
[navController didMoveToParentViewController:self];
}
}
Hope that helps.
I have a tab view controller with a navigation controller. In the first tab item I click on a button in a view that pops up a view with animated: YES.
Then when that view is done I hit another button that dismisses it. Like:
[self dismissViewControllerAnimated:NO completion:^{
ProfilesViewController *profile = [[ProfilesViewController alloc] init];
[self.navigationController pushViewController:profile animated:YES];
//SHOW YOUR NEW VIEW CONTROLLER HERE!
}];
But everytime this code runs, it dismisses the view, DOES NOT push the profiles controller, and shows the view from the first tab bar item.
How do I push the ProfilesViewController to the screen with a Back arrow?
If you are using dismissViewControllerAnimated to dismiss that means that the VC is presented modally. As such, it doesn't have a navigation controller (so self.navigationController is nil) and thus it can't push anything into the navigation controller.
You should really add a property to the controller which is a delegate or a completion block which can be used to push the controller from another controller (the one that presents it) to dismiss and push the controller.
A second option is to pass the navigation controller, it's a similar amount of code to using a block but not so good.
A crappy option is to use the parentViewController to find the appropriate navigation controller, but that sucks for many reasons.
I have a view controller (which is the root view of the main UINavigationController for my app) that is presenting another view controller hierarchy modally, by presenting a UINavigationController. After the user interacts with the root view controller in the modal NavController, a detail VC is pushed onto the modal NavController's stack. After some interaction with this detail VC, I'd like to push this detail VC back onto the original hierarchy, and dismiss the modal NavController/it's root view controller, all without the user seeing the change in this hierarchy. Right now I have something like this:
In MyViewController1 (root of the main NavController hierarchy):
UINavigationController *newModalNavController = [[UINavigationController alloc]
initWithRootViewController:someRootViewController];
[self presentViewController:newModalNavController animated:YES completion:nil];
Then in the someRootViewController above, after some interaction (e.g. a button click), I push the detail view controller onto the modal hierarchy:
[self.navigationController pushViewController:detailVC animated:YES];
Finally, in the detailVC, after some more interaction:
UINavigationController *mainNavController = /* Get main nav controller here (i.e. non-modal one) */
// Dismisses the modal view hierarchy (detailVC gets -(void)viewDidDisappear)
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
// Repush the detailVC back onto the main hierarchy (detailVC gets -(void)viewDidAppear)
[mainNavController pushViewController:self animated:NO];
// Keyboard was on screen before interaction; make it stay on screen after pushing
// detailVC onto main nav hierarchy
[self.textView becomeFirstResponder];
This all works (clicking back on the detailVC's navigationItem after pushing it onto the mainNavController's hierarchy goes back to the original rootViewController, MyViewController1), except when the keyboard is on screen in the detail view controller as it is being switched, it gets hidden and then animates back up, instead of just staying on screen (because the view is disappearing for a second as the presentingViewController dismisses the modal nav controller, whose hierarchy the detailVC is a part of, and then reappears as it gets pushed onto the mainNavController's stack, and the textView grabs first responder again).
Is there a better way to change which navigation hierarchy the detail view controller is a part of, possibly one that doesn't involve the view disappearing, and thus the keyboard being hidden and immediately reshown?
I'm trying to use a popover as an intermediary menu between my main view and a modal view controller. I can successfully present the Modal view controller from the popover by using the following code:
UIStoryboard *storyboardiPad = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad" bundle:nil];
cbwEditControlPanel *editCP = [storyboardiPad instantiateViewControllerWithIdentifier:#"EditCP"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:editCP];
[nav setToolbarHidden:NO];
[nav setModalPresentationStyle:UIModalPresentationFullScreen];
[nav setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:nav animated:YES completion:nil];
self.modalInPopover = NO;
The problem I'm running into is that when the EditCP modal view controller is dismissed, the main view controller never updates. I have a pagecontroller on the main view that should be updated to reflect the number of pages as set in the EditCP modal view controller, but for some reason the modal view controller being called from the popover prevents the main view controller from updating the pagecontroller. I've even tried calling the main view's "View Will Appear" method from the popover or modal view when they are dismissed, but even if the 'viewWillAppear' method is called the pageController will not update!
Any ideas what is preventing the pageController from updating? I even passed a reference to the pagecontroller to the modal view and tried to update it there, but it seems that from the time the popover is presented until it is dismissed, I cannot update the number of pages on the PageController.
Thank you!
So this is an old question but I also came across a similar problem recently when using a popover. My solution was to use an unwind segue to trigger my parent view to perform some action. In my case my parent view contains contact information and the popover contains a list of cites. All I wanted to do was to have the parent view update with the new city once the user selected it from the popover. So in my parent view I create my unwind function as follows:
In the .h:
- (IBAction)unwindToContactTVC:(UIStoryboardSegue *)unwindSegue;
In the .m:
- (IBAction)unwindToContactTVC:(UIStoryboardSegue *)unwindSegue
{
[self updateTableForOffice];
}
In the above .m file is where you would have the logic to do whatever it is you want to in the parent view. To connect this unwind segue go to the child view in the storyboard and control drag from the view icon to the exit icon. You should see a pop up with the name of your unwind segue.
Finally, give that unwind segue a name and then in the child controller in the viewWillDisappear() function call the segue as follows:
- (void)viewWillDisappear:(BOOL)animated
{
[self performSegueWithIdentifier:#"unwind-to-contact-tvc" sender:self];
}
I hope that helps. If someone has a better solution let me know.
Well, I half solved the problem. The only way to get an update function when the popover disappeared was to stop using Storyboards and programmatically present the popover, using the main view as the delegate. I then was able to update correctly inside the popoverControllerDidDismissPopover method.
However, I am still interested in finding a way to update the pageControl when the modal is dismissed, before the popover is dismissed.