I have two viewcontrollers. Here is the second one:
UIViewController *modal = [[ModalViewController alloc]init];
[self presentViewController:modal animated:YES completion:nil];
Second modal window:
[self dismissViewControllerAnimated:YES completion:nil];
How do I pass parameters from a modal view controller?
If I understand you correctly, you would like to make / tell the presenting controller to dismiss the modal view controller from the modal view controller?
I gather that being the reason why you thought of passing self as "parameter" to the modal view controller, so that it could reference it and make it dismiss the ModalViewController through the use of [self dismissViewControllerAnimated:YES completion:nil]; as you mentioned?
If so, you could make use of protocol:
In your modal view controller's header file (.h), declare:
#protocol ModalViewControllerProtocol
#require
- (void)dismiss;
#end
#interface ModalViewController
In your presented controller, which is ModalViewController in your case, declare the following in .h:
#property(assign, nonatomic) id<ModalViewControllerProtocol>myDelegate;
and make your presenting controller so that it adopts to the protocol:
#interface presentingViewController <ModalViewControllerProtocol>
And:
ModalViewController *modal = [[ModalViewController alloc] init];
modal.myDelegate = self;
[self presentViewController:modal animated:YES completion:nil];
When your modal view controller is presented and you would like to tell your presenting controller to dismiss it, you could do:
[self.myDelegate dismiss];
And finally implement the dismiss method in your presenting view controller:
- (void)dismiss
{
[self dismissViewControllerAnimated:YES completion:nil];
}
One could also forget about all of the aforementioned and simply call the following in the presented view controller (which, in your case, the ModalViewController):
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
But there are cases that self.presentingViewController is not returning the same controller that presented the modal view controller. Hence, using the protocol method would assure that we would like the same presenting view controller to dismiss the presented controller.
Related
In a view that was created modally, pressing a button causes the modal view to be dismissed and another modal view to load.
- (void)loadLanguageSelectionView {
[self dismissViewControllerAnimated:YES completion:nil];
UIViewController *languageSelectionController = [[LanguageSelectionViewController alloc] initWithNibName:nil bundle:nil];
[languageSelectionController setModalPresentationStyle:UIModalPresentationCustom];
[languageSelectionController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:languageSelectionController animated:YES completion:nil];
}
The following error is thrown when this code block executes:
DenkoStation[4259:73173] Warning: Attempt to present <LanguageSelectionViewController: 0x7b185430> on <ViewController: 0x79f52e50> whose view is not in the window hierarchy!
What surprises me is the fact that the code was running happily before I made some changes to my code as outlined here.
Where's the mistake?
Because you are trying to present a viewController on top of a viewController which is already dismissed and no longer in window hierarchy.
What you can try is, you can take the ParentViewController reference from current viewController and then you can present new viewController on ParentViewController Like This :
- (void)loadLanguageSelectionView {
UIViewController *parentController = self.presentingViewController;
[self dismissViewControllerAnimated:YES completion:^{
UIViewController *languageSelectionController = [[LanguageSelectionViewController alloc] initWithNibName:nil bundle:nil];
[languageSelectionController setModalPresentationStyle:UIModalPresentationCustom];
[languageSelectionController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[parentController presentViewController:languageSelectionController animated:YES completion:nil];
}];
}
I am presenting a modal view controller on the iPad which changes the presenting view controller while presented. For example:
A view controller VC presents the modal view controller when the user selects a cell in a table view.
The user selects an item on the modal view controller and another VC instance is opened in place of the first. Importantly, the view controller instance replacing the first is of the same type.
The modal view controller cannot be dismissed or an EXC_BAD_ACCESS exception occurs.
The failing dismiss is understandable: the presenting view controller is no longer available. Basically, how would I dismiss this presented modal view controller from a different presenting view controller?
The code I already have is:
ViewController1.m
- (void)showModalViewController:(UIViewController *)viewController
{
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
Thanks for your suggestions but I solved the issue by using delegation. The presented view controller defined a delegate to notify the presenter when an action occurred.
ChildViewControler.h:
#protocol ChildViewControllerDelegate <NSObject>
- (void) childView:(ChildViewController *)childView didSelectItem:(Item *)item;
#end
ChildViewController.m:
// in interface
#property (nonatomic, weak) id <ChildViewControllerDelegate> delegate;
// in implementation
- (void)closeView:(Item *)anItem
{
[self.delegate childView:self didSelectItem:anItem];
}
ViewController1.m:
- (void)showModalViewController:(UIViewController *)viewController
{
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
// Different view controller types may be passed here so check is required...
if (viewController.class == [ChildViewController class]) {
((ChildViewController *)viewController).delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)childView:(ChildViewController *)childView didSelectItem:(Item *)item
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
// Perform action required with 'item'
}
You can try to change
self.presentingViewController
(The view controller that presented this view controller or its farthest ancestor.)
property in your modal View Controller before dismissing.
Here is your problem:
//...
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
//...
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
You try to dismiss presenter view controller (that is currently seems to be swithced to another already) instead of presented modal view controller (in your case it UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];)
so (if presenters view controllers not the tabs of tabViewController or not stored in navigationController's stack or somewhere else) you must to store reference to it somewhere else than in presenter view controller which will be switched and could be deallocated.
As per document presentingViewController is a readonly property.
You could not modify it.
#property(nonatomic,readonly) UIViewController *presentingViewController NS_AVAILABLE_IOS(5_0);
I have not tested this code but there may be error in dismissModalViewController.
please put break point on this method the first line is perfect may your second line may cause error,may self.tableView is not accessible or self.tableView indexPathForSelectedRow may be nil.
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
Thanks.
Here's my set-up:
Nav Controller: ViewcontrollerA -> ViewControllerB
ViewcontrollerB displays a imagePickerController, setting itself as Delegate
If the user hits CANCEL from the UIImagePicker, I was to dismiss ViewControllerB completely, popping the user back to ViewControllerA.
In:
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
I have called:
[self dismissViewControllerAnimated:YES completion:nil];
and
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
and
[[[self parentViewController] parentViewController] dismissViewControllerAnimated:YES completion:nil];
No luck, ViewControllerB still hangs around.
How to I completely dismiss B programmatically from itself?
Is ViewControllerA presenting ViewControllerB or is ViewControllerB being pushed via your navigation controller? It sounds like it's being pushed. In this case I would try
[[self navigationController] popViewControllerAnimated:YES];
Edit for clarity:
You do want to dismiss the image picker the way you already are. You want to use popViewControllerAnimated to get rid of ViewControllerB.
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.
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
I'm developing a single view iOS 5.0+ app and I'm going to navigate throw my ViewControllers this way:
SearchViewController* search =
[[SearchViewController alloc] initWithNibName:#"SearchViewController"
bundle:nil];
[self presentViewController:search
animated:NO
completion:nil];
My question is, if I'm opening SearchViewController from HomeViewController, is HomeViewController dismissed after SearchViewController is shown?
I have a lot of UIViewControllers and I don't know if all of them will be on memory while user is navigating between them.
If You want to Present Only one Viewcontroller you can try like,
SearchViewController* search =
[[SearchViewController alloc] initWithNibName:#"SearchViewController"
bundle:nil];
[self dismissViewControllerAnimated:NO completion:^{
[self presentViewController:search
animated:NO
completion:nil];
}];
When you present a ViewController from another ViewController, they never get released from memory. To release them from memory you need to explicitly dismiss them.
The method presentViewController:animated:completion: sets the
presentedViewController property to the specified view controller,
resizes that view controller’s view and then adds the view to the view
hierarchy.
So you see you are getting a stack of ViewControllers and adding a View on top of another.
-(void)backAction:(id)sender
{
SecondViewController *viewCtrl = [[SecondViewController alloc] init];
[self.presentingViewController dismissModalViewControllerAnimated:NO];
[self.presentingViewController presentModalViewController:viewCtrl animated:YES];
}
I want to dismissModalViewControllerAnimated the current controller,then present a new controller. But it only dismissModalViewControllerAnimated:NO.
why?
self.presentingViewController will be nil after you dismiss yourself in the first line. So all you have to do is get a direct reference to whatever that controller is before you do the dismissal. In the following example, the presenting view controller is of the ViewController class. You will have to use what ever class is your actual presenting view controller.
-(void)backAction:(id)sender {
ViewController *presenter = (ViewController *)self.presentingViewController;
SecondViewController *viewCtrl = [[SecondViewController alloc] init];
[presenter dismissModalViewControllerAnimated:NO];
[presenter presentModalViewController:viewCtrl animated:YES];
}
In this line, you are already dismissing the view controller
[self.presentingViewController dismissModalViewControllerAnimated:NO];
Then in the next line you are trying to preset a modal view using the view controller that was already dismissed.
[self.presentingViewController presentModalViewController:viewCtrl animated:YES];
Instead try presenting the modal view using a the parent view controller (if you have any) of both these views. You can also fire a notification to your parent view controller after dismissing the first view so that you can launch the second modal view from the parent view controller.
Maybe you can try this:
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{
[self.presentingViewController presentModalViewController:viewCtrl animated:YES];
}]