For some reason, when I try to open a view controller via modal segue, it opens up two of the same type. Why is this happening?
Warning: Attempt to present <ModalViewController: 0x7fa062c5edd0>
on <HomeViewController: 0x7fa062e16e40> which is already presenting
<ModalViewController: 0x7fa062fb9780>
This is causing problems because I try to use delegates, but my main view controller never gets the correct delegate.
The issue occurs when I click the the button which triggers showModalView
HomeViewController
- (IBAction)showModalView:(UIButton *)sender {
ModalViewController *modalView = [[ModalViewController alloc] init];
[self presentViewController:modalView animated:YES completion:nil];
}
I tried this solution here and here and a dozen other ones, but none seem to work for me.
Why is this happening?
The problem you're having, is because you've connected a segue to the button, and are also presenting the controller in code; you should be doing one or the other. When you removed the segue, you got a black screen because you're using alloc init to create your controller. If you made the controller in a storyboard, then you should use instantiateViewControllerWithIdentifier: instead.
However, the easier way would be to leave the segue connected to the button, and delete the code you have in the button's action method. The button doesn't need an action method, if you have it hooked directly to a segue. All of this is covered in Apple's document, "View Controller Programming Guide for iOS". You should read it.
Related
I have a a VC structure like this
UIPageViewController -> detailViewController -> popoverviewcontroller
The popoverviewcontroller is dismissed using an unwind segue, bringing us back to the detailviewcontroller
Now, after the popover is done being dismissed, I would like to refresh the pages on the pagecontroller, since the action the user takes has changed the data.
I would also like to display an alert notifying the user about whether they were successful.
So I tried putting this code in the pageViewcontroller
- (IBAction) unwindFromPopup:(UIStoryboardSegue*)unwindSegue{
[self refreshPages];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:#"alert" message:#"this should appear" preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
}
I tried moving the above code to the detail view controller instead, but I am getting no result from this. No error or anything, just a complete lack of alert. I put a breakpoint in the segue, and the code gets called. But no alert.
I thought of putting the code in one of the respective viewDidAppear methods, but for some reason viewDidAppear does not get called for either the pageviewcontroller or the detailview controller after I dismiss the popup.
So at this point I have no idea how to make this alert appear.
Do I need to post my full code, or is my problem apparent with the details I've included?
Thanks - based on your comment ... long ago in a distant version of iOS I performed all the possible segues and noted what gets called when and have a table of that that I based my answer on. I must admit, nowadays I get most done using the presentation controller delegate.
Anyhow, to reply to your question, when you pop or modally present a controller, the controller that is being presented will message beingPresented and beingDismissed when it is done and you might be able to use this for what you are trying to do.
When you push a controller it will message isMovingToParentViewController when shown and isMovingFromParentViewController when dismissed, again in the controller being presented.
Back to a pop ... it will message prepareForSegue in the presenting VC and viewWillAppear and viewDidAppear in the presented VC and, when dismissing, will message only viewWillDisappear and viewDidDisappear in the presented VC, thus your problem. At least it will also message beingDismissed as mentioned and if you can use that I am really glad for you.
I am trying to write an app using UINavigationViewController. My first screen has several buttons on it, and on the click of each button, I want to segue to a UIViewController. I know that I can add a segue on each button, all pointed to the UIViewController that I want to go to, but I was wondering if it is possible to use only one segue that can be fired from each of the buttons.
If that is not possible, I was wondering if it was possible to open the second UIViewController from the first one, on button click, and provide a Back button like the UINavigationView provides. I did manage to get everything on this idea working, except for the back button. I mean I can put a standard button somewhere on the screen and go back, but I'd like the standard back button on the UINavigationView.
Phew! I'm not sure if that makes any sense.
I know that I could also use a tableview, but I'm trying to set this up with buttons.
Thanks
Edit: Thank you to everyone that answered. I now have this working. I would vote up the answers, but I don't have enough posts to do it. I appreciate the answers!
If you need to have separate action functions for each button, suggest that you segue from the main controller to the other controller and create a segue identifier (see xcode procedure below); then, use performSegueWithIdentifier from each of the button action functions. You can also take advantage of the prepareForSegue. To create the segue, control-drag from the left button in the controller in the storyboard to the controller you want to segue to and pick show.
Check the example code in swift that I did for a very similar problem in the SO reference
Linking View Controllers through button
You can embed the main controller in a navigation controller and that will give you the ability to navigate back. If you have multiple layers you can also use unwind segue.
Link each button to one single action (ex. buttonClick) in that ViewController and then perform the appropriate segue using pushViewController method on self.navigationController
-(IBAction)buttonClick:(id)sender {
if(sender.id == self.button1) {
DestinationViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"VC_IDENTIFIER"];
[self.navigationController pushViewController:vc animated:YES];
}
Or if you already have that 1 segue defined in storyboards you can use
[self performSegueWithIdentifier:#"SegueIdentifier" sender:self];
And use that inside the buttonClick method. Using the 1st example, or the second one as long as the segue you setup in the storyboards is a push then you should already get the back button as that is the default behavior for pushing view controllers onto the navigation stack.
I know there are a few other question out there like this, however I'm not able to get any of them to work. I think I need a little more clarification that I'm attacking this correctly.
I have a story board that I have a NavigationController on with a segue to a ViewController that contains a webview. I have successfully captured the the click on my webview and I would like it to open in a new ViewController that also contains a webview. I have added a second ViewController with a webview on to my storyboard, but can't figure out how to add a segue to it. I also tried making the connection in the code by adding the following to my shouldStartLoadWithRequest method of my first ViewController:
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self presentViewController:secondViewController animated:YES completion:nil];
However it never opens my second ViewController, it just stays in the first ViewController. I'm a noob and I'm sure I'm missing something somewhere?
Just add the segue from one view controller to the other (not connected to any specific button), and then give the segue an identifier.
Then, in your code, remove the initWithNibName, because you don't have a NIB. Instead, use performSegueWithIdentifier:sender:. You will also likely want to use prepareForSegue:sender: in order to pass the appropriate URL / HTML information that should be loaded into the second web view.
So i am creating an ipad recipe based app using storyboards. Just to give you an idea of the structure of my program here is the flow of View controllers:
ViewController --Modally--> PreviewViewController --Modally-->
RecipeViewController --Popup--> IngredientsViewController
All of this has been done in storyboarding. I created the IngredientsViewController and have linked it up to the RecipeViewController to be displayed as a popup, which works fine. However i want to be able to dismiss the IngredientsViewController programatically (because i've implemented voice command features). The problem is i can't seem to access the IngredientsViewController to dismiss it. (appologies i can't yet post any pictures).
I am using the following code to present the IngredientsViewController programatically (from within RecipeViewController):
[self performSegueWithIdentifier:#"ingr" sender:nil];
Which works fine to display the popup, but i cant dismiss it. I've tried to access it through the hierarchy of view controllers but cannot seem to find it, i would assume that it would be at the top of the stack, but apparently not? I've tried things like:
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
Again this was called from within RecipeViewController. But that simply dismisses the RecipeViewController and not the IngredientsViewController that is being presented by popup.
Just to be clear the IngredientsViewController is not a UIPopoverController, it is a normal viewController created in storyboard, and its segue style is popup linked to a button in the RecipeViewController.
Any help would be greatly appreciated. Thanks!
UIPopoverController *popOver = (UIPopoverController *)self.presentedViewController;
[popOver dismissPopoverAnimated:YES];
This should do the trick if your destination view controller is a UIPopoverController
Have you tried presentViewController:animated:completion: instead of performSegueWithIdentifier:?
I am creating an app using iOS 5 SDK. I managed to push views using the Storyboard's Segues, but I cannot find the proper way to pop the current view and go back to the previous one.
I am not using any navigationController (the app doesn't have any top or bottom bars).
I don't think using modal or push segue the other way would be the solution as it instantiates a new controller.
Do I have to use a custom Segue with the opposite animation and deletion of the view at the end ? Or is there a better way ?
Storyboards in iOS 5 don't provide a "no-code" way to return from a segue -- that's something you'll need to implement yourself.
If you use "push" segues (which require a navigation controller), use the navigation controller's popViewControllerAnimated: method to undo the last push segue. (Or other methods to undo more; see the UINavigationController documentation.)
If you use "modal" segues, call dismissViewControllerAnimated:completion: on the view controller which presented the current view controller (which you can get from its presentingViewController property).
Update: In iOS 6 and later there's unwind segues for going "back" in a storyboard. It's still not a no-code solution -- and it shouldn't be, because you need to be able to do things like differentiating between "Done" and "Cancel" exits from a modal view controller. But it does let you put more of the semantic flow of your app into the storyboard. Apple has a tech note that describes them in detail, and they're also covered in the video from WWDC 2012 Session 407.
You could try calling [self dismissViewControllerAnimated:YES completion:nil]; from the controller you want to dismiss (whether the controller has been pushed, or shown modally).
Here is the related documentation : UIViewController Class Reference
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.
Just to clarify.
In the class that was pushed. Simply wire up the following and the controller and view will be popped off.
[self.navigationController popViewControllerAnimated:YES];
Create Segue type "Custom" on your stroyboard. This can be from a button.
Create a new UIStoryboardSegue class named "popSegue"
In the popSegue.m file add the following;
-(void)perform{
UIViewController *sourceViewContreoller = [self sourceViewController];
[sourceViewContreoller.navigationController popViewControllerAnimated:YES];
}
-In the storyboard editor.
-Select the segue and change the Segue Class to "popSegue"
-Set the Identifier to "popSegue"
Done!
You can use the same "popSegue" class throughout your project.
Hope this helps
I'm using Xcode 5 also and here's how it's done. First, in the view code file that pushed the other, create an IBAction method in the .h file such as this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender;
Then in the .m file add this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender {
}
You can add any cleanup code you want executed in this method. Next go to your storyboard and select the pushed view. I assume you've got some kind of button on the view that the user taps to signal he's finished. Click on that button, hold down the key and drag to the the green box below the view which is the Exit. Release the mouse button but continue to hold the key. A popup will appear and your method will show in the list. Select that method. Now when the user clicks on the button, the view will pop and you'll be returned to the starting method.