Here is my problem. I had a UITableViewcontroller to which UINavigation controller is Embedded. To UITableviewController I had Add screen and an edit screen. Add screen is working perfectly. When I click on the records on the table view cell it is able to redirect to the edit page (detailed view). When I hit on Submit button the page is not navigating to the tableview cell. Here is the below error.
Warning: Attempt to present on whose view is not in the window hierarchy!
Here is the code I was trying for navigation.
UINavigationController *questionnaireNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"DLProjectsTasksubtasks"];
[questionnaireNavController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
DLProjectsTasksubtasks *qvc = (DLProjectsTasksubtasks *)[questionnaireNavController topViewController];
[qvc.tableView reloadData];
[self presentViewController:questionnaireNavController animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
Between I was using a Segue for transfering the data from tableview data to Edit Screen
I think your segue is being pushed on the navigation controller rather than being presented modally. So you have two options, either change the segue in your storyboard to be modal and use the dismissViewControllerAnimated:YES completion: or keep it the way it is and use popViewControllerAnimated: on your navigation controller.
[self.navigationController popViewControllerAnimated:YES];
For passing the data to the previous page you can either pass a reference of the previous view controller OR accessing it through your navigationController.viewControllers. Index n-2 will be your previous view controller (n is the count of viewControllers).
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.
WLINewPostViewController *newPostViewController = [[WLINewPostViewController alloc] initWithNibName:#"WLINewPostViewController" bundle:nil];
UINavigationController *newPostNavigationController = [[UINavigationController alloc] initWithRootViewController:newPostViewController];
newPostNavigationController.navigationBar.translucent = NO;
[tabBarController presentViewController:newPostNavigationController animated:YES completion:nil];
So I just simply push a new UIViewController.
Then after it posts the server callback calls a method with this code from the WLINewPostViewController.m:
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"Completed");
}];
[[self navigationController] popViewControllerAnimated:YES];
if (self == self.navigationController.visibleViewController){
NSLog(#"self = visibile");
}
if (self == self.presentingViewController.presentingViewController){
NSLog(#"self = presenting");
}
}
I tried a bunch of different things and none work.
I am relatively new to Xcode but after trying
[self dismissViewControllerAnimated:YES completion]
[self.navigationController popViewControllerAnimated:YES]
[self.navigationController.visibleViewController.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
and every other possibility, I am officially stumped. The WLINewPostViewController still won't dismiss.
It Logs out "self = visible"
Let me illustrate what you are trying to do
You have a navigation controller with Controller A.
Here you are trying to present another Controller B from Controller A.
Now when you get a callback from the server, you should call dismissViewControllerAnimated from Controller B to dismiss itself.
So after dismissViewControllerAnimated:completion: method call, the Controller B will be dismissed and Controller A will be shown automatically. Now you do not need to call popViewControllerAnimated: in completion block again as there is no other Controller in navigation controller to load.
If you have different use case, let me know I can provide solution.
You are presenting a view over navigationbar instead of pushing it over navigationbar.
When push you pop. When you present you dismiss. So instead of popViewControllerAnimated you need to use dismissViewControllerAnimated:completion
dismiss behaves differently depending on the receiver. From the docs:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
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. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
In short, if the vc on top calls it on itself, it dismisses itself. Anywhere else on the stack dismisses to that point, animating only the topmost vc.
What's extra confusing (for you and many others) is that the navigation vc has a stack too, and your problem is complicated further by presenting an navigation vc atop a tab-bar vc.
So what to do? The question is unclear about which vc is the receiver in the posted code (who is self in that snippet?). The text implies that self is a vc on the stack of the presented navigation vc, like...
TabBarVC --- presents ---> NavVC
| |
| --- viewControllers stack = rootVC, vc1
|
---> viewControllers for each tab
... and it's root or vc1 that wants to dismiss. If I'm right about that, then, given the docs, the solution is clear:
[self.navigationController dismissViewControllerAnimated:YES completion:^{}];
will put us back on the tabbar vc on whatever tab was visible when we did the present.
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.
I want to know what is the different of them.
when I can call
[self dismissViewControllerAnimated:YES completion:nil];
and when I should call
[self.navigationController popViewControllerAnimated:YES];
according document of apple:
dismissViewControllerAnimated means
"Dismisses the view controller that was presented by the receiver."
But I always fail to dismiss view controller by this method.
-dismissViewControllerAnimated:completion:
Used to dismiss an UIViewController, which was presented by the method:
-presentViewController:animated:completion:.
-popViewControllerAnimated:
Method of UINavigationController is used to pop a controller shown by
-pushViewController:animated method of UINavigationController.
In the first case the view controller's view shows as a modal controller (usually from bottom to top), and the second case you are pushing a view controller in the navigation stack of UINavigationController.
your selected application is navigation based application means
[self.navigationController popViewControllerAnimated:YES];
your selected application is other than the navigation based application means
[self dismissViewControllerAnimated:YES completion:nil];
I have an application with several view controllers controlled from a tab bar controller. From one of these view controllers I want to (on clicking a button) segue to another view controller and retain the tab bar at the bottom of the segued to view.
I've used
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"newView"]){
UIViewController *controller =segue.destinationViewController;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
}
}
This works fine except the tab bar is missing from the segued to view (a placeholder shows for it in the storyboard, but it doesn't show up when the app is run)
I've also tried replacing
[self presentModalViewController:controller animated:YES];
with
[self presentViewController:controller animated:YES completion:nil];
but that doesn't work either.
A bit of debugging shows that for the segued-to view controller, the tabBarController property is set to nil.
Is there anyway to retain the tab bar in the segued-to view controller?
From your explanation, I don't think you want a modal controller. Modal is used to overlay, rendering your tab bar useless. From your storyboard, select your segue and select push, not modal.
Push vs Modal (Note the tab bar):