I have a view controller B in a parent view controller A. Both views are showing the same time.
In view controller B, I'm trying to present a new view controller using the following method:
- (void) buttonClicked:(id)sender
{
MyViewcontroller *vc = [[MyViewcontroller alloc] init];
[self presentViewController:vc animated:YES completion:nil];
}
The view controller appears correctly in iOS6 and I dismiss MyViewController by using the following method:
- (IBAction)backButtonPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
But there is a difference between iOS 5.1 and iOS 6.
QN1: Upon dismissal, view controller A & B viewDidAppear are not invoked. Is it supposed to be triggered?
QN2: I can't get MyViewController to show up in iOS 5.1.1. unless I add view controller B as a child container to A:
[self addChildViewController:vcB];
[self.view addSubview:vcB.view];
By adding the child controller, I can get MyViewController to show and view controller A&B viewDidAppear will be called when it gets dismissed. viewDidAppear also gets called when using iOS6.
I'm not sure what is going on here.
Answer 1 : viewDidAppear will not be called when you dismiss a modal view.
Answer 2 : if you are presenting "MyViewController" from "View-controller B" then View-controller B's view should be in view hierarchy.
From here you can get more information. How to Presenting View Controllers from Other View Controllers
Related
I am presenting a view controller modally using this code :
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SubmitAYoNViewController *ivc = [storyboard instantiateViewControllerWithIdentifier:#"SubmitAYoN"];
[ivc setModalPresentationStyle:UIModalPresentationFullScreen];
[self presentViewController:ivc animated:YES completion:nil];
Then in my SubmitAYoNViewController I have this:
NSLog(#"%#",self.parentViewController);
if([self.parentViewController isKindOfClass:[YesOrNoViewController class]]) {
NSLog(#"do something");
}
self.parentVeiwController is NULL. Why is that?
EDIT : I want to access a method from my parent view controller, then one that the SubmitAYoN was opened from.
SOLUTION : I used delegates instead. As per answers below, the presentViewController does not create a parent-child relationship.
If you want the presenting view controller, then use self.presentingViewController. self.parentViewController returns the controllers parent which will be nil when it is not a child of another controller.
So use:
NSLog(#"%#",self.presentingViewController);
if([self.presentingViewController isKindOfClass:[YesOrNoViewController class]]) {
NSLog(#"do something");
}
From the documentation for presentViewController:
This method sets the presentedViewController property to the specified
view controller, resizes that view controller's view based on the
presentation style and then adds the view to the view hierarchy.
So no mention of the controller being made a child as it is the view that is added to the existing view hierarchy.
You would have been ok prior to iOS5. The documention for property parentViewController states:
Prior to iOS 5.0, if a view did not have a parent view controller and
was being presented, the presenting view controller would be returned.
On iOS 5, this behavior no longer occurs. Instead, use the
presentingViewController property to access the presenting view
controller.
self.parentViewController is something you presented from this viewcontroller or self.navigationgtionController
So when you are presenting a viewcontroller where you hold an instance of the `viewcontroller' will have self.parentViewController.
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 updating an iOS app and receiving the following warning when presenting a view controller: Presenting view controllers on detached view controllers is discouraged <UINavigationController: 0x984e640>.
The app delegate loads the main view controller like this:
[self.window setRootViewController:[MainViewController sharedInstance]];
[[MainViewController navigationController]setViewControllers:#[[MenuViewController new]]];
MainViewController creates a new navigation controller and in viewDidLoad the new navigation controller is added to the view with this:
[self.view addSubview:_navigationController.view];
Now I need to present another view controller. I have tried using this:
[_navigationController presentViewController:anotherViewController animated:YES completion:nil];
and this:
[[MainViewController navigationController] presentViewController:anotherViewController animated:YES completion:nil];
+ (UINavigationController*)navigationController {
return [[MainViewController sharedInstance]navigationController];
}
I get the same warning with both of them and the view controller loads, but I cannot navigate back. I'm obviously doing something wrong, but I'm not sure what it is.
I think you want to push it instead of modally present it : presentViewController is a method from UIViewController to present modal VCs, whereas pushViewController pushes VCs in the navigation stack.
Try this instead :
[_navigationController pushViewController:anotherViewController animated:YES];
From Home view - my RootViewController - I open up 2 ViewControllers one after another as user progresses in navigation hierarchy like so:
1) SecondViewController is pushed by button connected in my Storyboard
2) ThirdViewController is presented modally
[self performSegueWithIdentifier:#"NextViewController" sender:nil];
So, the picture is: RootViewController -> SecondViewController -> ThirdViewController
Now in my ThirdViewController I want to have a button to go back 2 times to my RootViewController, i.e. go home. But this does not work:
[self.navigationController popToRootViewControllerAnimated:YES];
Only this guy goes back once to SecondViewController
[self.navigationController popViewControllerAnimated:YES];
How can I remove both modal and pushed view controllers at the same time?
I had a similar situation, where I had a number of view controllers pushed onto the navigation controller stack, and then the last view was presented modally. On the modal screen, I have a Cancel button that goes back to the root view controller.
In the modal view controller, I have an action that is triggered when the Cancel button is tapped:
- (IBAction)cancel:(id)sender
{
[self.delegate modalViewControllerDidCancel];
}
In the header of this modal view controller, I declare a protocol:
#protocol ModalViewControllerDelegate
- (void)modalViewControllerDidCancel;
#end
And then the last view controller in the navigation stack (the one that presented the modal view) should implement the ModalViewControllerDelegate protocol:
- (void)modalViewControllerDidCancel
{
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController popToRootViewControllerAnimated:YES];
}
This method above is the important part. It gets the presenting view controller to dismiss the modal view, and then it pops back to the root view controller. Note that I pass NO to dismissViewControllerAnimated: and YES to popToRootViewControllerAnimated: to get a smoother animation from modal view to root view.
I had the same requirement but was using custom segues between the view controllers. I came across with the concept of "Unwind Segue" which I think came with iOS6. If you are targeting iOS6 and above these links might help:
What are Unwind segues for and how do you use them?
http://chrisrisner.com/Unwinding-with-iOS-and-Storyboards
Thanks.
Assuming your AppDelegate is called AppDelegate, then you can do the following which will reset the rootviewcontroller for the app window as the view RootViewController
AppDelegate *appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
RootViewController *rootView = [[RootViewController alloc] init];
[appDel.window setRootViewController:rootView];
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.