Difference between `present` & `push` ViewController - ios

dismissViewController method not working to get previous viewcontoller
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
popViewController work and go to previous viewcontroller
[self.navigationController popViewControllerAnimated:YES];
i want to pass data like below please help me.but as i describe dismissViewController method not working.
[self.navigationController dismissViewControllerAnimated:YES completion:^{
HomeVC *loading;
loading.IdNameLabel.text=display;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Code" message:display delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}];
thank in advance.
Update
-(void)viewWillAppear:(BOOL)animated{
self.IdNameLabel.text=self.GetscanResult;
}
this method contain in HomeVC.when dismiss navcontroller it shows HomeVC.
is upper method can call after dismiss viewcontroller?
SOLUTION
this is very stupid question when i am at beginner level.
here is specification about it.
it will depend on method which will you Present || Push viewController.
Delegate methods will work with protocol delegate method when view controller present.

The method you need to use to go back depends on how you showed the view controller.
The first of the two common ways are by using a navigation view controller to push and pop view controllers off the stack.
The other common way is to present and dismiss a view controller modally.
You cannot for example present a view controller modally, and expect to be able to pop it off from the navigation controller, or vice versa.

Related

Whose view is not in the window hierarchy issue?

Warning: Attempt to present on whose view is not in the window hierarchy!
Yes I have looked at the other questions and answers before posting this question. They have not helped me resolve this. Here's what I am doing. I am calling on my singleton class socialHelper to display an action sheet, where postToFacebook method is an option to select. When clicking on this action, I get the Warning above and the postToFacebook is not displayed. I'm calling this from a UIViewController with the UINavigationController as the main controller, and my SocialHelper class is a NSOject.
- (void)postToFacebook
{
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
slComposeViewController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[slComposeViewController setInitialText:#"Building stairs? Checkout StairsPro on the app store!"];
[slComposeViewController addImage:[UIImage imageNamed:#"StairsIcon120x120.png"]];
[slComposeViewController addURL:[NSURL URLWithString:#"https://itunes.apple.com/us/app/stairs-pro/id477032819?ls=1&mt=8"]];
[self presentViewController:slComposeViewController animated:YES completion:nil];
// I've also tried this, which shows the post window, but after hitting cancel or post, my previous viewController is not the current viewController. So my navigation bar is gone and can't move around in my app.
// [[[[UIApplication sharedApplication]delegate]window]setRootViewController:slComposeViewController];
}else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"No Facebook Account" message:#"No Facebook account has been configured. You can configure or create a Facebook account in settings." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alert show];
}
}
So what I'm asking is what is the best way to get the slComposerViewController to display in this case, also how we could use the UIApplication option as well. Thank you!
I would change the method to pass in the ViewController to make sure the correct VC is presenting it like so:
- (void)postToFacebookFromVC:(UIViewController*) presentingVC
And inside that method when calling presentViewController use:
[presentingVC presentViewController:slComposeViewController animated:YES completion:nil];
I assume then that using your socialHelper singleton class would be something like:
[[socialHelper sharedResource] postToFacebookFromVC:self]; //inside the VC that you want to display it
For your second question on using the rootViewController try this:
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:slComposeViewController animated:YES completion:nil];
Edit: Since the first two suggestions didn't work for your project, try getting the topMost ViewController and presenting slComposeViewController from it instead of "self" like so:
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
[topController presentViewController:slComposeViewController animated:YES completion:nil];
This answer won't resolve your issue but it should make some sense of why it isn't working and help you towards finding the right solution for what you originally intended.
SLComposeViewController being a UIViewController has an associated UIView that will be displayed on top of the view of the controller receiving the message presentViewController.
As correctly done in this line but, self needs to be a UIViewController (an object that has a view property).
[self presentViewController:slComposeViewController animated:YES completion:nil];
It will work if you move your -postToFaceBook method out of the SocialHelper class and put it inside your UIViewController class (file) that will display the action sheet, so that way self is actually a UIViewController instead of NSObject.
From Apple's UIViewController reference page:
When you call the presentViewController method on a UIViewController
it sets the presentedViewController property to the specified view
controller, resizes that view controller’s view and then adds the
view to the view hierarchy.
Your SocialHelper class is not a view controller so it does not have a view to which the action sheet view could be added to display it (on top).
I'm thinking even if you inherit your SocialHelper from UIViewController, the view of your SocialHelper would not be in your controller's view hierarchy.
If it is only two or three view controllers where you need to display the action sheet, I would replicate the code instead of creating a separate class; if it is several view controllers, then it makes sense to have the action sheet display logic as a separate class, you just need to find the right design pattern to do that.
Edit 1
Forgot to mention the declaration of your variable is missing the SLComposeViewController *
Is it just missing in your post or you have declared the variable somewhere else in your viewcontroller class?
Try declaring it right there in the same line you create the SLComposeViewController; that might fix the issue. If that did not work then double-check all you need to do to support Facebook in your app by looking at this very simple and easy to follow tutorial.
SLComposeViewController *slComposeViewController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
I'm assuming you created your navigation controller with main view controller as its root:
navigationController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
And to show the flip side controller you do this in your main view controller:
[[self navigationController] pushViewController:flipSideViewController animated:NO];
The flip side controller has the about button to show the about view when tapped; I'm thinking you do a presentViewController not a push:
[self presentViewController:aboutViewController animated:YES];
Finally, in the about view controller you should have your -postToFacebook method with the corrected line mentioned above; which will be called by the log (button?).

How to call method on presenting view controller from modal view controller

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...!

How do I change the action of a button with UIPickerView

I am trying to change where a button leads to depending on the selected row of a picker view. I am trying to display data depending on a certain year and I can make the picker display the available years and change a label to the corresponding year, but how can i make the "go" button I;ve created lead to the correct view controller?
Maybe you haven't had time to figure this out, maybe you have, but this is what I would do. You have a text field that you will populate with a picker, call this the "yearTextField." Then you have a button that you want to change the action depending on the year in question. Call this "myChangableButton." This is just for an example, you can call them anything you like. I put my initial view controller in a navigation view controller to have better control of the views. For this example I made two different years 1942, and 1971. They each have their own view controller that I named "FourtyTwoViewController" and "SeventyOneViewController" in my first view controller you will need to import these into the .m file. Like this..
#import "ViewController.h"
#import "FourtyTwoViewController.h"
#import "SeventyOneViewController.h"
Also, make sure when you drag out the new view controllers in the storyboard, that you name them as such. And don't forget to use that name as the Storyboard ID, found under the Identity inspector. Now for myChangableButton I made an action that will bring up whichever view controller you want based on the text in the yearTextField like this..
- (IBAction)myChangableButton:(id)sender
{
if ([_yearTextField.text isEqual: #"1942"])
{
FourtyTwoViewController *ftvc = [self.storyboard instantiateViewControllerWithIdentifier:#"FourtyTwoViewController"];
//[self presentViewController:ftvc animated:YES completion:nil];
[self.navigationController pushViewController:ftvc animated:YES];
}
else if ([_yearTextField.text isEqual: #"1971"])
{
SeventyOneViewController *sovc = [self.storyboard instantiateViewControllerWithIdentifier:#"SeventyOneViewController"];
//[self presentViewController:sovc animated:YES completion:nil];
[self.navigationController pushViewController:sovc animated:YES];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Watch Out!" message:#"Put in valid date" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
}
The line of code that I commented out will bring up a modal transition, but I prefer a push since that gives you a great back button. That's it. I hope this helps you on your project. Make a comment if you have any questions, and good luck. And WELCOME! to SO!!
EDIT #1
Here is a sample of how I set the initialUrl in a viewcontroller that I pushed.
VideoViewController *vvc = [self.storyboard instantiateViewControllerWithIdentifier:#"VideoViewController"];
vvc.initialUrlString = urlString;
vvc.descriptionString = descriptionString1;
[self.navigationController pushViewController:vvc animated:YES];
I imported VideoViewController into the first view controller. And VideoViewController has two properties called initialUrlString and descriptionString. In the first view controller I have two properties called urlString and descriptionString1, these are set in the first view controller during another method and then passed into the second.

Dismissing both UINavigation views and Modal views at once programmatically

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.

dismissViewControllerAnimated VS popViewControllerAnimated

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];

Resources