I have a modal view controller that I called in from another view controller. Upon dismissal of the modal view controller, I want a method to be called on the view controller that presented that modal view. What is the easiest way to do this?
I tried doing this in my modal view controller: [(ParentViewController*)self.presentingViewController foo]; before calling [self dismissViewControllerAnimated:YES completion:nil];.
Xcode gives me an error saying foo isn't recognized, even though it is defined and prototyped in the controller. If your solution involves blocks, I really don't understand them so I would appreciate it if you would add more detail. Thanks.
ParentViewController.h
#interface ParentViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
{
NewAssignmentViewController *newAssignmentViewController;
TableViewDataSource *data;
}
-(void)foo;
#end
You need to get a correct reference to your presenting controller like this:
ParentViewController *presenter = [(UITabBarController *)self.presentingViewController viewControllers][0]; // 0 is assuming that ParentViewController is in the first tab. Change if necessary
[presenter foo];
The other way to do it would be to use delegation, but that's an answer for another time.
If you are using a Storyboard segue to present your view controller you could dismiss it using an Unwind Segue. An unwind segue is a special kind of segue that unwinds the presented view controllers back to a presenter.
To accomplish this, you would create a method in the presenting view controller with the following signature:
- (IBAction)unwindAction:(UIStoryboardSegue*)unwindSegue;
This is different than a standard IBAction because the parameter type is a UIStoryboardSegue* instead of the normal id type (it doesn't have to be named unwindSegue:, it could be modalViewFinished: or whatever you like - the important part is that it has a return type of IBAction and a parameter type of UIStoryboardSegue*).
Once you have this method defined, in your storyboard you control-drag from the modal view controller icon (below its view, in the little bar of icons) and release the connection on the green exit sign. This will create an unwind segue, which you should give an identifier in the attributes inspector. Unwind segues will not show up visually in the storyboard canvas, so you will have to find it in the list of items on the left side of the canvas (this is collapsed by default, expand it by clicking the little circular button in the lower left hand corner of the canvas).
Once you've done that, rather than calling [self dismissViewControllerAnimated:YES completion:nil], just call [self performSegue:<Identifier you gave the unwind segue>] instead. During this process the unwindAction: method defined on the presenting view controller and the prepareForSegue: method on the modal view controller should be invoked. You can do whatever cleanup you need to do in these methods (calling the foo method from unwindSegue:, for example).
You call the method on the UIViewController that is your MainView, and pass it your UIViewController you want to be the ActionSheet.
UIActionSheet *actionSheetController =[[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"show otherview", nil];
[self presentModalViewController:actionSheetController animated:YES ];
To dismiss the UIActionSheet, dimissWithClickedButtonIndex:animated: is a method for the UIActionSheet that you can implement. The method can be called by whoever (so if you want to dismiss it from your mainview have a reference to the action sheet and do something like
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
{
switch (buttonIndex){
case 0:
{
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
}
break;
case 1:
{
MyClass *myclassObject = [[MyClass alloc]init];
[myclassObject foo];
}
}
}
The method is also called whenever the 'cancel' button is clicked by the user.
Use this ModalViewControllers link for better understanding...!
Related
I know how to change between tabs when I am currently on one of the views presented by the tab controllers, using this:
self.tabBarController.selectedIndex=1;
But right now I need to change between tabs while on a modal child view, I need the modal view to be dismissed and the other tab to be shown,
The best/proper pattern for this is to have the modal child hand off the task to its delegate as it exits. Define your own simple "myChildViewDelegate" protocol (could be a single method, even), and give the modal child a "delegate" property like so:
id<myChildViewDelegate> delegate;
When the user presses a button or whatever on the modal view, it calls a method on its delegate and the delegate dismisses the modal view and changes tabs.
Assuming you have some sort of button on the presented view controller just set that buttons target to a function in the presenting view.
-(void) someFunction {
... Code that creates the modal view controller and buttons
[modalViewController.changeTabButton addTarget: self action:#selector(changeTab:) forControlEvents:UIControlEventTouchUpInside];
[self presentViewController:modalViewController animated:YES completion:nil];
}
-(void) changeTab:(id)sender {
self.tabBarController.selectedIndex=1;
}
My problem
I have a standard UIViewController. With the press of a button, it loads a form sheet modal view controller. When dismissing this modal view with the press of a UIBarButtonItem I call a method by doing:
ViewController *main = [[ViewController alloc] initWithNibName:nil bundle:nil];
[main updateLabel];
In the method -(void)updateLabel in the main ViewController I'm setting the text of a label, but the label won't change. But I know the function gets called, because if I do a NSLog(#"Method call test); instead of label.text = #"Test" I can see the message in console.
What am I doing wrong? It must be the way I'm calling the method in the main ViewController, because I can easily change the label anywhere else.
What I want to do:
When dismissing a modal view controller, I want a method to be called in the main view controller, and in this case change the text of a label.
Thanks for your help!
You're creating a new instance of ViewController with that code, not getting a pointer to the one you already have.
If ViewController is the controller that presented the modal view, then you can get a pointer to it with,
ViewController *main = self.presentingViewController;
A better way to do this would be to use the delegate pattern.
https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html
The following is a design pattern suggestion
The modal view controller shouldn't know how to dismiss itself, that is the responsibility of the presenting view controller. After all, it could have been presented in many different ways (modally, popover, push navigation). Using the delegate pattern, the modal view controller would tell its delegate that it should be dismissed when the bar button item gets pressed. The delegate, which is the presenting view controller, would then dismiss the modal view and update the label mentioned in your question.
I have a view controller in my application where on my screen I have a UIView that the user is required to tap on. When they do that, I want to call another viewController's view, and display it on the screen for the user. Unfortunately, I am having trouble displaying the view.
The name of my viewController that I am making the call from is called "MainViewController", and the ViewController whose view I wish to display is called, "NextViewController"
Here is my code from where I make the call:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"I was touched.");
_nextView = [[NextViewController alloc] init]; //this code is not being called
[self.view addSubview:_nextView.view]; //neither is this being called
}
Where _nextView is a property that I declare in the .h file of MainViewController.
This method is being called, but for some reason because I am able to see the log statements print to the output, but for some reason I am unable to call the lines after that. What am I doing wrong?
You shouldn't add the view of another view controller to your view without making that view controller a child view controller. If you just want a view, then set one up in a xib file and add it to your view as a subview. If you want to use a view controller, then you should present it modally, and dismiss it when you're done. This kind of situation where you want to gather some info from the user to use in your app, is an appropriate place to use a modal view controller. MainViewController should set itself as the delegate of NextViewController, and NextViewController should define a delegate protocol to send the data back to MainViewController.
To present it modally, do this:
_nextView = [[NextViewController alloc] initWithNibName:#"your nib name here" bundle:nil];
[self presentViewController:_nextView animated:YES completion:nil];
Are you using a Navigation Controller? Or Storyboards? One way of displaying another view controller would be like this:
[self presentViewController:_nextView animated:YES completion:^{
}];
A couple of things:
- If your NSLog gets called, then so do the other two lines you say do not.
- I assume you mean you want to display the other view controller on screen, not display the other view controller's view on the first view controller. These are two very different things, the second of which you wouldn't want to do.
I have tried a few answers on this site, but none of them seem to relate to my problem
I have a MasterDetail app which has a two types of segues that I am using. When you press a button on the detail view, it uses a push segue and pushes another detail view onto that. In the new detailview (the one that was just pushed on) there is a button which calls up another UIView (form sheet) using a modal segue.
What I am trying to do is when the user selects a row, a UIAlertView will show up displaying a message, while at the same time (doesn't have to be at the same time) it dismisses the UIViewcontroller (modal) and goes back from the Viewcontroller that was pushed on. Basically, I need to be able to dismiss all viewcontrollers, one modal and one push (nav) so that the view returns to the original main screen that they started with.
I have the UIAlertView working fine and I can get the modal viewcontroller to dismiss by using [self.dismissViewControllerAnimated:YES completion:nil]; but I don't know how to dismiss the next Viewcontroller (which is in a navigation controller). Using this: [self.navigationController popToRootViewControllerAnimated:NO]; does not work.
Here is where I want to call the function to remove all views:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlWithIDAndChallenge];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
UIAlertView *message = [[UIAlertView alloc] initWithTitle#"Started" message:#"The challenge has begun!" delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil];
[message show]
//right here is where I would normally dismiss the modal VC
//here is where I would like to dismiss all VC
}
If you want, in iOS 6.0 (and later) projects you can use an unwind segue. For example, you can:
In your top level view controller (the one you want to unwind to, not the controller you're going to unwind from), write an unwind segue method, in this case called unwindToTopLevel (personally, I find it useful if the segue bears some indication as to what the destination of the segue is, for reasons that will become apparent when we get to step 3, below):
- (IBAction)unwindToTopLevel:(UIStoryboardSegue *)segue
{
NSLog(#"%s", __FUNCTION__);
}
In the Interface Builder scene from which you will initiate the unwind, control ⌘-drag from the view controller icon to the exit icon to create the unwind segue:
Generally you'd define the segue from a button to the exit outlet (and you're done), but if you want to invoke the segue programmatically, you might want to create it between the controller and the unwind outlet, like shown above.
You'll get a little pop up that includes the name of your unwind segues (from the presenting controllers ... it's like magic):
If you're going to invoke that segue programmatically, you have to select that unwind segue in the document outline on the left side of the IB window and once you've done that, you can give the unwind segue a storyboard id:
I generally use the name of the the unwind segue for the storyboard id, but you can use whatever you want.
Now, having done that, your alert view can invoke the segue:
- (IBAction)didTouchUpInsideButton:(id)sender
{
[[[UIAlertView alloc] initWithTitle:nil message:#"go home" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil] show];
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex])
{
[self performSegueWithIdentifier:#"unwindToTopLevel" sender:self];
}
}
From the class you presented your modal view
call dismiss of modal and then perform selector after some delay and then do the
here is the sample code
//Write this in the class from where you presented a modal View.
//Call the function from modal class when you want to dismiss and go to root.
- (void) dismissAndGoToRoot {
[self dismissViewControllerAnimated:YES completion:nil];
[self performSelector:#selector(gotoRoot) withObject:nil afterDelay:0.50];
}
- (void)gotoRoot {
[self.navigationController popToRootViewControllerAnimated:NO];
}
Here you will call the function
//right here is where I would normally dismiss the modal VC
//here is where I would like to dismiss all VC
[self.parentViewController dismissAndGoToRoot];
If this does not work then take an instance variable of ParentViewController in modal class and assign when presenting modal view controller.
You could try replacing [self.dismissViewControllerAnimated:YES completion:nil]; with
self dismissViewControllerAnimated:YES completion:^{
[parentViewController.navigationController popToRootViewControllerAnimated:YES];
}
I believe parentViewController will point to the presenting view controller, and the block will cause the parent view controller's navigation controller to pop to the root view controller.
I have a button in my popover controller. I want to use it to dismiss the popover, so I am trying to access a method (dismissPopover) of the presenting view controller (the "root" view controller).
Note: the method to dismiss the popover is already set up and working, in the root VC, which is the delegate. If I call it it will dismiss the popover. I just need to access the method from the popover.
To do this I set up a property in the AppDelegate, and get an instance of the rootVC like this: self.rootController = (ViewController*)self.window.rootViewController;. Then I imported the root VC class and the AppDelegate to the popover's view controller's class, as below. Seems to give me access to the rootVC, and the methods, but the results do not fire the method. Any idea what I am missing here?
#import "ViewController.h"
#import "AppDelegate.h"
Action connected to button:
- (IBAction)dismissPopover:(id)sender {
//Checking the button works, it does:
NSLog(#"dismissPopover, from popover");
//Trying to get an instance of the rootViewController, the "presenting view controller"
ViewController *rootVC = [(AppDelegate *)[[UIApplication sharedApplication] delegate] rootController];
//trying to access the method in the rootVC that dismisses the popover
[rootVC dismissPopover];
//Tried the following code, does nothing:
//[self dismissPopoverAnimated:YES];
}
NOTE: I ended up abandoning the use of a popover for this as it became a bit over complicated. I tried loading my view controller into a UIView (so I could load the contents of a nib to a pop-up view). That also became a bit complicated. So, for now I am just building my desired interface in a UIView programatically. So far works great.
dismissPopoverAnimated: is a method of UIPopoverController class. so, you need a popover controller reference in your 'root' view controller.
MyRootViewController.myPopoverController = thePopover;
the button is in your 'root' view controller, and in it's action method:
[self.myPopoverController dismissPopoverAnimated:YES];
In iOS 8, you can dismiss the popover (if it's coming from a segue, at least) with dismissViewControllerAnimated:completion: from within the popover. Doesn't work in iOS 7 (or below), however.
Popover automatically dismissed when clicking outside it , as you order a button to dismiss it you can simply use the following code inside your dismissPopover method :
[self.popoverController dismissPopoverAnimated:YES];
you don't need all this tedious work !
[self dismissViewControllerAnimated:YES completion:nil];
is the solution;
you just need an IBoutlet or add target to your button and then call above line
I had the same problem
just do in your buttonClickMethod:
[yourPopoverController dismissPopoverAnimated:YES];
hope you help!
cheers