Lets say I have two UIViewControllers, we'll call them VC1 and VC2. VC1 performs a segue that then goes to VC2, and VC2 has a close button that will unwind that segue back to VC1.
So normally, if I'm wanting to make a custom UIStoryboardSegue I would make a segue from VC1 to VC2 and then select custom and use my custom UIStoryboardSegue I made that and I have it working just fine.
And for the close button I would put in code in the destination UIViewController (VC1) like this:
#IBAction func prepareForUnwind(unwindSegue: UIStoryboardSegue) {
//some code
}
Then on the UIViewController with the close button, I would ctr + drag up to the EXIT and select that unwind prepareForUnwind. Then I'd select the UnwindSegue on the left panel and change the class to my unwind class.
The problem is, in my case I am having to create that button with code, so I don't have the option of ctr + drag. So how do I connect it to the exit and then assign the class for the unwind segue with code only? I've tried googling it, but everything keeps coming back with the method when you are using the storyboard.
I can include more code if necessary, just let me know.
You can connect your button in this way:
First Step
In the Storyboard connect with drag and drop your source view controller name icon with the exit icon, there you should see in the pop-up your prepareForUnwind function.
Then you can set for your unwind segue an identifier.
Second step
Add an #IBAction function for your close button and perform the segue:
#IBAction func closeAction() {
performSegue(withIdentifier: "yourExitIdentifier", sender: self)
}
Related
I am trying to present two view controllers. I'm trying to avoid presenting one after the other as this doesn't give a good user experience.
I'm using storyboards / segues to present the view controllers, each embedded in navigation controllers.
The behaviour should be:
View Controller 1 presents view controller 2 - but when view controller 2 dismisses I'd like view controller 3 to be the one showing to the user. And ideally an ability to also dismiss to view controller 1.
I understand I can accomplish this with child views. But I'd ideally like to learn how it can be done by manipulating the navigation stack.
I don't think you can do what you want with segue, but certainly you can do it with a little code...
This will (on a button tap, for example) perform a standard slide-in navigation controller animation directly from the current ViewController (call it vc1) to ViewController2, but "insert" ViewController3 into the stack. Tapping the Back button will take you from vc2 to vc3 to vc1.
#IBAction func didTap(_ sender: Any) {
guard let vc3 = storyboard?.instantiateViewController(withIdentifier: "vc3"),
let vc2 = storyboard?.instantiateViewController(withIdentifier: "vc2")
else { return }
let vcArray = [self, vc3, vc2]
self.navigationController?.setViewControllers(vcArray, animated: true)
}
If you want to go from vc2 back to vc1 and "skip over" vc3, in vc2 add (on a button tap, for example):
#IBAction func backToStartTap(_ sender: Any) {
self.navigationController?.popToRootViewController(animated: true)
}
As best to my understanding about your question, try out below method:
Present VC 3 from VC 1 and from VC 3 present VC 2 immediately(This can be done by putting the viewController present code in the ViewDidLoad() of VC 3).
So when you dismiss VC 2, VC 3 will be shown and on dismissing VC 3 , you will be redirected to VC 1.
VC means ViewController.
Presenting viewController in the ViewDidLoad() is really a bad idea.
You can present VC3 and add VC1.view and VC2.view as a subview of VC3's view and later you can remove VC1.view and VC2.view as you want and behaviour will be same as you are expecting.
You need to set your the viewcontroller you are going to present .modalPresentationStyle as .currentContext, and you need to set your current viewcontroller .definesPresentationContext as false
I have created a sample application with the following screen setup
Clicking on the load popover button simply calls the following code:
#IBAction func clicked(sender: AnyObject) {
self.performSegueWithIdentifier("test", sender:self)
}
As you can see in the segue properties, all that segue does is loads the viewcontroller with the anchor view being the button testAnchor3
The problem is: I dont know why the view is not anchoring. When ever i click on the LoadPopover button, the other view controller does load as a popover, but it anchors in the middle of the screen and not under the button like its supposed to. How do i get the popover to anchor correctly?
Create a outlet for your button and put it in presentViewController: toView as a view.
- (void)presentViewController:(NSViewController *)viewController asPopoverRelativeToRect:(NSRect)positioningRect ofView:(NSView *)positioningView preferredEdge:(NSRectEdge)preferredEdge behavior:(NSPopoverBehavior)behavior
{
//Change the ofView to be your button outlet
[super presentViewController:viewController asPopoverRelativeToRect:positioningRect ofView:popOverButton preferredEdge:preferredEdge behavior:behavior];
}
I created story board App. Actually I have issuw with segue. I have one "A" view controller, and segue is suppose to push "B" viewcontroller. In "A" viewController, we are expecting some userInfo and we are performing some validations for that, and based on validation only it should push new viewController through segue. My issue is, I used triggered segue and it always push "B" view controller. Can you please inform me based on validations, how can I disable segue.
Connect the button from Scene A. Not from a UI Element on Scene A, but from the base view of Scene A.
Now, in Scene A's view controller, if you've got a button that's supposed to validate, just use an if or whatever. If all the info is good, call the segue, if it's not, don't call the segue.
Call the segue like this:
[self performSegueWithIdentifier: #"MySegue" sender: self];
Where #"MySegue" is replaced with the name you gave your segue on the storyboard.
For example:
- (IBAction) myButtonPress:(id)sender {
//validate the info
if(valid) {
[self performSegueWithIdentifier: #"MySegue" sender: self];
} else {
//probably prompt the user that their info is invalid
//maybe clear textboxes, etc.
}
}
They key here though is attaching the segue from the viewController, and not from the button itself. You also have to be sure to name your segue, and be sure the name on storyboard matches the name in code. XCode won't help you autocomplete it.
Is there a way to have one button unwind back to a specific view controller? For example suppose I have ViewController A and B. Both modally segue to ViewController C. Now I understand how to segue unwind back to one of the previous view controllers (As was explained here) but how do I go back to the specific view controller that presented VC C?
So to sum it up, what I'm trying to figure out is...
If A segued to C then I want to unwind back to A when the button is selected. If B segued to C then I want to unwind back to B when the button is selected.
You should use the standard way to return from a modal segue. Put this in your 'back' button...
[[self presentingViewController] dismissViewControllerAnimated:YES];
This doesn't use the storyboard or a segue for the return, just code.
You could use the presentingViewController 'title' property to steer the unwind process to the desired originating ViewController. This solution would also allow you to pass data from your ViewController C to ViewControllers A and B.
1) Select the View Controller button (the yellow one) at the top of the storyboard canvas of ViewController A.
2) In the Attributes Inspector (View Controller section) give ViewController A a title.
3) Include the following code in ViewController A:
#IBAction func returned(segue: UIStoryboardSegue) {
// Don't need any code here
}
4) Repeat steps 1, 2 and 3 for ViewController B.
5) Override the prepareForSegue function in ViewController C as follows:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if self.presentingViewController!.title! == "ViewControllerATitle" {
let destination = segue.destinationViewController as! ViewControllerA
destination.someFunction(someArgument)
}
else if self.presentingViewController!.title! == "ViewControllerBTitle" {
let destination = segue.destinationViewController as! ViewControllerB
destination.someFunction(someArgument)
}
}
I'm working on this tutorial, which shows how to create an app with storyboards. My viewController has two UIButtons and I have connected a segue to those UIButtons. These segues push a new viewController.
Now I am looking for a way to cancel this segue if a certain condition becomes true. When I used the old xib files to create my interface I was able to do something like this:
-(IBAction)sendButton:(id)sender {
if(TRUE) {
// STOP !!
}
}
This does not work anymore. How can I cancel the push of the new viewController?
Nope. You can't cancel segues that are directly linked to interface elements like your UIButton.
But there is a workaround.
Remove the segue that is linked to the button
Add a segue from the viewController (the one with the button) to the viewController that should be pushed. This must be a segue that is not connected to a interface element. To do this you can start the segue from the status bar. Or create the segue in the left sidebar.
Select this segue, open the attributes inspector and change the Identifier of the segue (e.g. PushRedViewController)
Select Xcodes assistant view, so you can see the .h file of the viewController with the button.
Connect the button to an action. To do this select the button and control-drag to the .h file. Select action in the menu and name your action (e.g. redButtonPressed:)
Open the implementation of your viewController
change the action from step 5 to something like this:
- (IBAction)redButtonPressed:(id)sender {
[self performSegueWithIdentifier:#"PushRedViewController" sender:sender];
}
You can actually do this by adding following method into your code
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if([identifier isEqualToString:#"btnSegue"])
{
return YES;
}
else{
return NO;
}
}
Lets assume your segue is btnSegue.And if you need the segue to be performed based on some condition you can have following code
if(check)
{
[self performSegueWithIdentifier:#"btnSegue" sender:self];
}
Where check is BOOL which you can set true or false based on your condition.