Swift: performSegueWithIdentifier during unwind not working - ios

I've created a typical unwind flow from my view controller #2 to back to view controller #1 using a programmatically created button using the segue to Exit technique.
I have trace statements that confirm that the button code is executing perfectly and that the #2 performSegueWithIdentifier func is being called with the correct segue ID, but nothing happens.
To clarify:
I've connect the view controller #2 to the Exit and confirmed the segue ID is exact in all places. I traced the #2 identifier in the #2 performSegueWithIdentifier func and it matches perfectly.
As I understand it, I no longer need to use the dispatch fix with the current version of 2016 XCode. I tried it anyway and nothing worked. There is no crash, just no unwinding.
Somehow the unwind technique isn't reversing using this Exit technique. Any ideas?
I've been following the tutorial here: https://spin.atomicobject.com/2014/12/01/program-ios-unwind-segue/
CODE VC #2:
// action func wired to button, fires perfectly
func unwind(seg:UIStoryboardSegue!) {
self.performSegueWithIdentifier("unwind", sender: self)
}

In VC1 you need a function:
#IBAction func unwindToVC1(segue:UIStoryboardSegue) {
}
Then in your storyboard ctrl drag from the yellow view controller icon to the Exit icon and select unwindToVC1: from the pop up
Give the unwind segue an identifier, say unwindToVC1
Now, in VC2, create your button touchUpInside handler:
func buttonTapped(sender:UIButton) {
self.performSegueWithIdentifier("unwindToVC1")
}
when you set up your button programatically, add this method as the action handler:
button.addTarget(self, action: "buttonTapped:", forControlEvents: .TouchUpInside)

Move the unwind function to VC1. Leave that implementation empty or perhaps put a print statement to see if its coming to that function.
Make sure that in the storyboard there is a unwind segue created with identifier "unwind"
Add a button action func buttonAction:(button:UIButton) that calls performSegueWithIdentifier("unwind"...) in VC2.
Set the buttonAction as the action for the button created programatically.

Related

xcode 8 swift: unwind segue transfer data is not working

So I have been trying to get a unwind segue to work from last two days. I have tried watching and follow several different tutorials and none was successful. I created the #IBAction override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) but this never performs any of the code. I put a simple print and had it print text, it never printed the text. I created that in the first View Controller then in the second View Controller I had a button that I connected to the Exit at the top of the View Controller and selected the unwind action and it never unwinds. What am I doing wrong or missing?
So here is what I have so far I have 2 views: RaceViewController & SingleStatViewController
I will start by selecting specified rows in RaceViewController and they will open SingleStatViewController modally. I have a button in SingleStatViewController called dismissBtn. When I click the button it dismisses SingleStatViewContoller. I just need it to pass some data back to the RaceViewController.
So if the user selects 1 of 3 cells in the table (the rows are Half-Orc, Half-Elf and Human) it will open SingleStatViewController and ask to select 1 stat (these will be of 6 buttons (Str, Dex, Con, Int, Wis, Chr) when they select the one of the buttons it will return the data and update the detailTextLabel.
I select Human then I select Str. When it gets back to RaceViewController and it updates the text in the detailTextLabel to +2 Str.
Right now, this is the code I am using to dismiss. Can I use this to continue or do I actually need to perform the unwind:
#IBAction func dismissbtn(_ sender: Any)
{
dismiss(animated: true, completion: nil)
}
You need to perform unwind and send data as following:
#IBAction func unwindToRaceViewController(sender: UIStoryboardSegue)
{
if let sourceViewController = sender.sourceViewController
as? RaceViewController {
stat = sourceViewController.stat
}
You could refer to this article for unwind segues as alternative to delegate
You could also use delegates to pass data back to the previous controller.
Okay, so you have vc A and vc B, this is what you need to do to use unwind segue:
In vc A, create a segue function eg. #IBAction func unwind(segue: UIStoryboardSegue)
In vc B, write code that call self.performSegueWithIdentifier("unwind", sender: self) (assume that the identifier and the function name is the same - call this in your dismissbtn)
In vc B in storyboard, look on top of it, there will be an yellow button and 2 orange button, hold and drag the yellow button to the Exit orange button, choose the action that you created in vc A, give it an identifier as mentioned
In the unwind func, the rest is the same as normal segue when you pass data forward, just change destination to source and you must present vc B from vc A, else then use delegate

how to unwind segue to the view controller that called it

Below is a great answer of how to use Apple's unwind segue.
What are Unwind segues for and how do you use them?
My problem with the answer however is that you have to explicitly tell storyboard which view controller you want to exit (unwind) back to. What I'm trying to do is exit (unwind) back to whichever view controller called it, only using one button.
Let's say I have 3 view controllers: Red, Blue, and Yellow.
Both Red and Blue have a button on them to go to the Yellow view controller, but Yellow only has one button, return. Is it possible to have the yellow return button unwind back to whichever view controller called it?
thanks to #luk2302 I was able to figure it out. No need to even use unwind segue. Thanks luk2302!
#IBAction func returnViewController(sender: AnyObject) {
if((self.presentingViewController) != nil){
self.dismissViewControllerAnimated(true, completion: nil)
}
}

performSegueWithIdentifier has no segue with identifier

I encountered a crash while testing my app. The following image is my storyboard:
I have the code in View Controller:
#IBAction func unwindToHomeScreen(segue:UIStoryboardSegue) {
}
The view of "Add new Item" have a "Cancel" button, I controlled-drag it to the "Exit" icon at the top and link to unwindToHomeScreen, it works fine.
The "Add new Item" has a class AddTableViewController and the code I wrote inside:
#IBAction func save() {
// Execute the unwind segue and go back to the home screen
performSegueWithIdentifier("unwindToHomeScreen", sender: self)
}
I controlled-drag the "Save" button to the func, but the app crash when I click the button
I can use dismissViewControllerAnimated(true, completion: nil) instead, but I just wonder why performSegueWithIdentifier can't work?
I read the code at dropbox.com/s/hpybgg9x67rtqng/foodpinstatictable.zip?dl=0 and try to make one and using performSegueWithIdentifier like this example for practicing, I didn't see the segue identifier at her, what is the difference?
You haven't actually given that segue an identifier. unwindToHomeScreen is a function that you can call; what you're looking for is your segue identifier, which is set in Interface Builder like this:
In this example, I have a button wired to the next view via Interface Builder like you describe ("Interface Builder") and a button that is wired to this IBAction in my view controller ("Programmatically"):
#IBAction func goToNextView(sender: UIButton!) {
self.performSegueWithIdentifier:("go", sender: self)
}
In any case, what you're missing is the actual identifier, which can be set in the attributes of a segue created in Interface Builder.
Swift 4:
Sometimes we must clean the build folder and then try again.
Worked for me after ctrl-dragging the new segue and giving it a name, then using it programatically as:
performSegue(withIdentifier: "goToMyNewViewController" , sender: self)
I found that because I renamed my view controller, I needed to delete and recreate the segue.
A possible issue with iOS 12 and earlier (iOS 13 seems not to suffer from the same issue) might come from the fact that performSegue(withIdentifier:,sender:) is called from the view controller viewdidLoad() callback.
You absolutely must invoke it from the viewDidAppear(_ animated: Bool) callback in order to avoid the question-mentioned crash.
That will certainly save hours of hair-puling…
in my case reorder the Embed in
Tab bar controller
Navigation controller
vc (contains button - tapping button initiates a "show, e.g. push" segue)
Too fix the title in the navigationBar
I had the same issue.
All I did was:
I selected the segue indicator in the storyboard.
Deleted it.
Then created a new segue with the same identifier. Boom, it works.
I had it all wired up correctly. It was just that the viewController I was segueing from, was NOT instantiated from the storyboard. I had it instantiated pragmatically.
I initially had it written as:
let vc = DeviceVC()
Had to change it to:
let sb = UIStoryboard(name: "Main", bundle: Bundle(for: DeviceVC.self))
let vc = sb.instantiateViewController(identifier: "DeviceVC") as! DevieVC
for me --> click on the relation-arrow between view controllers and then select attribute inspector, there is identifier give name and select push class done...

Swift: Perform a function on ViewController after dismissing modal

A user is in a view controller which calls a modal. When self.dismissViewController is called on the modal, a function needs to be run on the initial view controller. This function also requires a variable passed from the modal.
This modal can be displayed from a number of view controllers, so the function cannot be directly called in a viewDidDisappear on the modal view.
How can this be accomplished in swift?
How about delegate?
Or you can make a ViewController like this:
typealias Action = (x: AnyObject) -> () // replace AnyObject to what you need
class ViewController: UIViewController {
func modalAction() -> Action {
return { [unowned self] x in
// the x is what you want to passed by the modal viewcontroller
// now you got it
}
}
}
And in modal:
class ModalViewController: UIViewController {
var callbackAction: Action?
override func viewDidDisappear(_ animated: Bool) {
let x = … // the x is what you pass to ViewController
callbackAction?(x)
}
}
Of course, when you show ModalViewController need to set callbackAction like this modal.callbackAction = modalAction() in ViewController
The answer supplied and chosen by the question asker (Michael Voccola) didn't work for me, so I wanted to supply another answer option. His answer didn't work for me because viewDidAppear does not appear to run when I dismiss the modal view.
I have a table and a modal VC that appears and takes some table input. I had no trouble sending the initial VC the modal's new variable info. However, I was having trouble getting the table to automatically run a tableView.reloadData function upon dismissing the modal view.
The answer that worked for me was in the comments above:
You likely want to do this using an unwind segue on the modal, that
way you can set up a function on the parent that gets called when it
unwinds. stackoverflow.com/questions/12561735/… – porglezomp Dec 15
'14 at 3:41
And if you're only unwinding one step (VC2 to VC1), you only need a snippet of the given answer:
Step 1: Insert method in VC1 code
When you perform an unwind segue, you need to specify an action, which
is an action method of the view controller you want to unwind to:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
//Insert function to be run upon dismiss of VC2
}
Step 2: In storyboard, in the presented VC2, drag from the button to the exit icon and select "unwindToThisViewController"
After the action method has been added, you can define the unwind
segue in the storyboard by control-dragging to the Exit icon.
And that's it. Those two steps worked for me. Now when my modal view is dismissed, my table updates. Just figured I'd add this, in case anyone else's issue wasn't solved by the chosen answer.
I was able to achieve the desired result by setting a Global Variable as a boolean value from the modal view controller. The variable is initiated and made available from a struct in a separate class.
When the modal is dismissed, the viewDidAppear method on the initial view controller responds accordingly to the value of the global variable and, if needed, flips the value on the global variable.
I am not sure if this is the most efficient way from a performance perspective, but it works perfectly in my scenario.

Unwind segue from navigation back button in Swift

I have a settings screen, in that I have a table cell. By clicking on that I take to another screen where user can choose an option and I want it back in the previous view controller when back button is pressed.
I can put a custom bar button item, but I want to return to the parent view controller using the back button in the navigation bar rather than with a custom button on the view.
I don't seem to be able to override the navigation back button to point it down to my unwind segue action and since the back button doesn't appear on the storyboard, I cant drag the green Exit button to it
Is it possible to unwind a push segue with the back button?
Here's my solution, based on Objective-C code from Blankarsch to this StackOverflow question: How to trap the back button event
Put this code inside the View Controller you want to trap the Back button call from:
override func didMoveToParentViewController(parent: UIViewController?) {
if (!(parent?.isEqual(self.parentViewController) ?? false)) {
println("Back Button Pressed!")
}
}
Inside of the if block, handle whatever you need to pass back. You'll also need to have a reference back to calling view controller as at this point most likely both parent and self.parentViewController are nil, so you can't navigate the View Controller tree.
Also, you might be able to get away with simply checking parent for nil as I haven't found a case where pressing the back button didn't result in parent being nil. So something like this is a bit more concise:
override func didMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
println("Back Button Pressed!")
}
}
But I can't guarantee that will work every time.
Do the following in the view controller that has the back button
Swift 3
override func didMove(toParentViewController parent: UIViewController?) {
if !(parent?.isEqual(self.parent) ?? false) {
print("Parent view loaded")
}
super.didMove(toParentViewController: parent)
}
I tried the same and it seems that you cannot catch the standard back button's action so the solution will be to use a custom button and bind it to a segue which leads back to the previous page.
You could use some sort of delegation as you did or use a custom back button and an unwind segue.
Better even, you could handle passing data between your view controllers using a mediator:
http://cocoapatterns.com/passing-data-between-view-controllers/

Resources