So I have a "Save" button on a view controller, we'll call VC2. When clicked, it unwinds back the original view controller, we'll call VC1.
However, I want to run some logic on the data from VC2 before I pass it back to VC1. I tried mapping an IBAction from the Save button that exits VC2 and runs the unwind code on VC1. But I noticed it doesn't run the IBAction mapped to the button. I'm guess this is because it's linked to exit.
So my question is: if I have a button that exits my view controller, can I also have it linked to an IBAction so that I can run some logic on the view controller being exited (VC2) before it exists?
If your button is linked to a segue on the storyboard, its IBActions does not seem to run. I don't know if this is documented anywhere, but that seems to be the behaviour.
Instead of linking the button to the "Exit" of VC1, link your VC2 to the "Exit", then give that unwind segue an identifier, let's say unwindToVC1.
Now you can add your IBAction to the button, and write:
#IBAction func buttonPress() {
// process your data or whatever
// perform the unwind segue
performSegue(withIdentifier: "unwindToVC1", sender: yourData)
}
Related
I want have the back button on 'Saved Locations' to unwind segue to the 'Second View Controller', but not sure if I have to do anything different because it is embedded in navigation controller. I have linked the back button to the unwind function already in the storyboard. (ViewController is 'Saved Locations'). Currently, when clicking the back button, nothing happens.
The code below is in the SecondViewController.swift file
#IBAction func fromView(segue: UIStoryboardSegue){
if let sourceViewController = segue.source as? ViewController{
print("hi")
}
}
Segues work in context of the push/modal and popover segues. Your SavedLocations controller is embedded in a UINavigationViewController, but SecondViewController is not child of the same container. It's actually not very clear how you take the user to the SavedLocations from your storyboard screenshot but I'd like to propose embedding both controllers under the same navigation controller as possible. This is the the simplest and the cleanest way, as you don't even have to handle unwind seagues manually at all. "Plain" navigation is handled automatically in backward direction for you (unless you want to pop more than one controller in one go)
My question is very much like these two:
IOS - How to segue programmatically using swift
How do I create a segue that can be called from a button that is created programmatically?
EXCEPT that my segue is to a Navigation Controller, like this: Main->NC->VC2>VC3.
The segue from Main to VC2 works fine when I trigger it with a button in my first view controller. It does not work when the segue is called by code. I need to have the segue triggered automatically in response to certain conditions determined by the code in Main.
When the button is linked to a function coded in Main, it still works. Code in the button function definition runs and the segue occurs. When I try to trigger the segue in the code for the function, using
performSegueWithIdentifier("showHelp", sender: nil)
or
performSegueWithIdentifier("showHelp", sender: self)
I get an error:
Warning: Attempt to present <UINavigationController: 0x7fc8e1043000> on <myApp.Main: 0x7fc8e0d17b10> whose view is not in the window hierarchy!
The button continues to trigger the segue, despite the warning, but not when the button's function is called by other code.
self.navigationController?.pushViewController(nextViewController, animated: true)
So I have a login view, after successful login it goes to the first view of a navigation controller, then the user can go deeper to a settings view and then to a logout view. This logout should take the user back to the login view (which is not part of the navigation controller). It works with this code:
let loginViewController = self.storyboard!.instantiateViewControllerWithIdentifier("Login") as? LoginViewController
self.navigationController!.pushViewController(loginViewController!, animated: true)
But the login view displays the navigation bar at the top, which it shouldn't do, maybe there is something other than self.navigationController!.pushViewController that I should be using?
SWIFT: You should use an unwind segue.
First of all, put the following line in your FirstViewController:
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
The function actually doesn't have any code inside it.
Now, go to your storyboard and create an unwind segue for LogoutViewController by control-dragging from the yellow button to the Exit button. Like this:
Select the unwind segue created for FirstViewController.
Change the segue identifier:
Go to the code of LogoutViewController and just call the unwind segue normally:
self.performSegueWithIdentifier("unwindToViewController1", sender: self)
Swift 4
self.performSegue(withIdentifier: "unwindToViewController1", sender: self)
If you have a Navigation controller, from your your controller use:
self.navigationController?.popToRootViewControllerAnimated(true)
Look into unwind segueing if you are working with storyboards.
You just need to create unwind option in controller, that you want navigate to:
#IBAction func unwindToMe(segue: UIStoryboardSegue){}
Then create segue from storyboard.
And when you need to navigate back, just call the performSegue method with the unwind segue identifier that you just created.
If you want to do it only from code, than you just can write something like:
let loginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("Login")
UIApplication.sharedApplication().keyWindow?.rootViewController = loginViewController
In this case, you will set your app to initial state.
try it
self.view.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil)
This will get you back to the beginning of the application flow.
Updated to Swift 4 (thanks #javaBeast)
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
self.navigationController?.popToRootViewController(animated: true)
is the best option to go to first controller of navigation controller
and then dismiss the navigation controller
I recommend you to make a segue from one ViewController to another, instead of pushing your ViewController like that.
So first, you need to Ctrl + clic from your first ViewController to your login ViewController, and then in the attribute inspector your give it an Identifier.
Then, all you have to do is this :
self.performSegueWithIdentifier("yourIdentifier", sender: self)
Now, for the navigation bar, I suggest you to remove the navigation controller from the login view, and associate it to your first view. It would remove the navigation bar from your login view.
This highlighted line is where should popToRoot proceed, after a successful registration it should redirect to Root View Controller. For some reason it's not working for me, literally nothing happens, not even error.
I tried with
self.navigationController?.popToRootViewControllerAnimated(true)
You don't appear to be using navigation controller at all, so I'd wager that self.navigationController is nil.
You could use an unwind segue. So in your root view controller, add a method like so:
#IBAction func unwindToRoot(segue: UIStoryboardSegue) {
print("successfully unwound")
}
Then in your scoreboard scene from which you want to unwind, you can control-drag from the button to the "exit outlet":
When you let go, you can pick the unwind action:
This achieves the "pop to root" sort of functionality, but is not contingent upon using navigation controller.
If you want to perform this unwind programmatically, rather than doing the segue from the button to the exit outlet, do it from the view controller icon to the exit outlet:
Then, select the segue in the document outline and give this segue a unique storyboard id:
Then you can programmatically perform the segue, using the same identifier string:
performSegueWithIdentifier("UnwindToRoot", sender: self)
this is odd, but I'll try to explain the best I can.
I have a navigation controller which has a view. The view asks a simple question. That view then has two segues available -- one on the view for a correct answer, which is a "show" on the navigation controller and the other for an incorrect answer which is a "popover".
The segues are tied to the view and the answer box for the show and popover respectively.
I'm testing the answer on button press and using performSegueWithIdentifier to then show the appropriate window.
if (answerField.text == "2") {
println("Correct")
performSegueWithIdentifier("Correct", sender: sender)
} else {
println("Incorrect")
performSegueWithIdentifier("Error", sender: sender)
}
The problem I'm having is that if I get the answer correct, it moves fine to the next view, but it shows the "incorrect" popover view after segueing to the correct one.
I know that sounds complicated, but it's a super simple app at present. I suspect I'm just doing it wrong. Of note is that I also get an error "Presenting view controllers on detached view controllers is discouraged" which may be related.
Many thanks for any assistance anyone can provide.
D.
It sounds like you have one or both of the segues hooked up directly to the button in your storyboard, which will trigger the segue automatically on the button press even when you don't call performSegueWithIdentifier(_:) in code. You are then setting up an IBAction method which is also called on the button press, where you are then programmatically performing the segue again.
You can check this by going to your storyboard and selecting the button. Go to the connections inspector on the right, and I expect you'll see a Triggered Segues section, with an "action" triggering your "show" segue. You can remove this by clicking the cross next to the connection.
For segues that should be initiated programmatically, you should create a segue by dragging from the view controller object (rather from a control / actionable element) to the next view controller. This will create a generic segue that is only triggered by performSegueWithIdentifier(_:) in code.
As you have noticed, since your segue is for a popover presentation it will complain unless it is explicitly anchored to a particular view in your storyboard. You can still drag the segue from the view controller object to the popover view controller, but you must manually hook up the anchor: select the segue in the storyboard, choose the attributes inspector on the right, and drag from the circle in the "Anchor" field to the text field you want to anchor to.