Now I created a new project to test unwind segue.
In Storyboard the story entry is on VC0, a button in VC0 goes to VC1 (modally), a button in VC1 goes to VC2 (modally).
VC2 has a button to dismiss itself and in the function it looks like this:
#IBAction func btnDismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
in VC1 I added an unwind function as follows:
#IBAction func unwindSecondView(segue: UIStoryboardSegue){
print("unwinded")
}
Then in storyboard I linked VC2's exit icon to unwindSecondview as action.
It works pretty well, however after I clicked "dismiss" button in VC2, VC1 appeared briefly and jumped back to VC0.
??? Anything wrong that caused jumping back to VC0?
--------------Initial question -----------------
I'm new to iOS and got a little confused for how VCs are created and activated.
Say I created 2 ViewControllers in my single view app. MainController(VC_M) and SettingsController(VC_S). I added the segues in storyboard, a setting button in VC_M goes to VC_S, VCS collects information, writes to standardDefaults, then ok button goes back to VC_M and refresh its view.
When I try to preserve some information in VC_M, I found that after I click ok button in VC_S and go back to VC_M, VC_M gets recreated and viewDidLoad() gets called. In the debugger, it shows the VC_M object (self) now has a different address in memory, so seems it's another instance of the class.
Is this a normal behavior? It's best that the old VC_M object gets saved so some data doesn't need to be reloaded. Initially I thought I could put some data loading stuff in init(), but when VC_M gets re-created init() got called, too. Some older posts suggested that ViewDidLoad() should not be called again but rather ViewDidAppear() should get called, but I'm not sure if it's the same case now.
In my case is it better to just use standardDefaults to reload everything, or is there a different kind of segue I should use? (I used push segue).
Related
I'm trying to get an unwind segue working (in Swift) and have read through all the "unwind segue not working" posts I could find but haven't yet found whatever it is I'm doing wrong. So.
I have a UISplitViewController with a UITableViewController as my left-hand menu and two UINavigationController+UITableViewController detail views that I'm switching between. Segue-based navigation from my left-hand menu works fine. It's when I want to programmatically unwind that things do not.
I've created an #IBAction function in my first view controller, the one I want to unwind to, like this:
#IBAction func unwindToDocuments(_ segue: UIStoryboardSegue) {
}
(My first view controller is my "Documents" view.)
Then, in the second view controller, I connect an unwind segue to the exit node with identifier unwindSegueToDocuments and action unwindToDocumentsWithSegue:.
It's my understanding that should be all I need to do. However, when I try:
self.performSegue(withIdentifier: "unwindSegueToDocuments", sender: self)
it doesn't do anything. It stays in my second view controller as the detail view.
I'm using self.performSegue there because I'm in a closure. It's some network work so it's being done in a DispatchQueue.async() background queue. However, the performSegue call is in a nested DispatchQueue.main.async() block at the end. (Or, rather, I've tried it both/all ways, and still can't get it to unwind.)
Nothing helpful appears in the console.
I've read several posts on this issue but none of them solved my problem.
I'm coding an app where I have to click on a button ("Prepare") to go to the following ViewController. When the button is clicked, it also passes data between the two view controller.
The problem is, when I click the button, the following ViewController loads twice. Thus, if I want to go back I have to go back through two same ViewController.
I've checked the segue, the class names and files names but nothing fixes it.
I've also created a new project and rewritten all the code from the beginning but in the end, it still doesn't work.
However, I've noticed that the problem showed up when I added the prepare(forSegue:) function and the performSegue function. Without it the ViewController only loads once. But of course, I can't get the data passed between the views without it...
Here is the screenshot of my two view and the code of the two functions :
First view
Second view
// Prepare the segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "prepareSegue" {
let timerViewController = segue.destination as! CountdownViewController
timerViewController.timeInterval1 = waitingTime
timerViewController.timeInterval2 = shootingTime
timerViewController.lastSeconds = lastSeconds
timerViewController.currentTimeInterval = waitingTime
}
}
// Prepare the timer when the button is pushed
#IBAction func prepareTimer(_ sender: Any) {
performSegue(withIdentifier: "prepareSegue", sender: self)
}
Probably, when two ViewControllers appear it's because you have:
A segue on Storyboard which start directly from a Button
IBAction attached to the button where you call a performSegue of the same Segue
To fix this problem, you need to create a Segue which start directly from ViewController. After you can use the IBAction and the performSegue
You have added a seague but also an IBAction. If the seague is defined well in InterfaceBuilder it will perform and call your method. The IBAction is the alternative way for connecting an Action to a button. If you use both, you have two actions.
Without the rest of your project to check, one thing it could be is a completion closure which contains the performSegue(...) statement is called twice, so the performSegue statement is literally run twice. How might this happen?
If you have a networking call inside the closure which contains performSegue command, and the networking call has a closure which calls completion(...) twice, then you could get either the segue occuring twice or even recursively!
It's rare this will be the case, or in your instance, but this type of problem can be uncovered by using lots of breakpoints and following the "bouncing ball" to see the completion() calls occuring more than once.
I am trying to create an unwind segue that takes me back two viewControllers, and I thought I had everything set up correctly, but it crashes with no error printed when I perform the segue...
Here's what I've done:
In the DESTINATION viewController, we'll call it VC1 (bottom of the stack) I created this action:
#IBAction func unwind(segue:UIStoryboardSegue){}
In the interface builder I hooked up VC3 (the third view controller up) to the unwind segue via the exit button and named it "unwind".
I call perform for segue like so:
performSegue(withIdentifier: "unwind", sender: self)
Here is the view controllers and segue identifier
http://imgur.com/a/gJPYQ
The "Delete Trip" button calls the segue AFTER all the other logic has been taken care of.
As soon as that is called, my app crashes with no error message and shows me the AppDelegate screen. What's going on?
If you pushed your VC, you could use:
navigationController?.popToRootViewControllerAnimated(true)
If you have two VC's presented modally, then you will need to dismiss two VC's modally. Use:
dismiss(..., completion: {
dismiss(..., completion: nil)
Your current "unwind" segue is simply presenting the original one again..and maybe you don't have identifiers set up correctly or you are overlooking your view hierarchy.
i'm fairly new to swift, so please bear with me.
right now my problem can be broken down to this:
I have one Test View Controller that can display my 'Test' object by showing the description and the title. I also have an array of Test objects. In the top right hand corner, there is a 'skip this test' button, and if the user clicks on it, the viewcontroller will segue to itself, but change the Test object that is being displayed, e.g. its just basically looping through an array of Tests. I have changed the prepare for segue methods accordingly to push the data through the view controllers.
However, I want to be able to move to a completely different ViewController (lets just call it FinalViewController) if I have reached the last Test in my Test array.
Now this is the part that I can't fix.
I would like to create a segue directly from the 'skip test' button, only that it segues to a different view controller, depending on a certain condition. However, as i tried creating a segue in IB by right clicking on the Button and pulling it to the FinalViewController, it erased my previous segue I had for that button.
Does anybody know if there is a fix for this problem? thank you!!!!
However, as i tried creating a segue in IB by right clicking on the
Button and pulling it to the FinalViewController, it erased my
previous segue I had for that button
Actually, you don't want to do that, instead, you should drag from the controller itself, not from the button because if your segue has been created based on a button, tapping on it should always perform it and this is NOT what you want.
After creating a segue from the ViewController, you can -programmatically- call performSegue method and handle what data should be passed to the next viewController by implementing prepareForSegue method.
For more information, check my answer to know how to create multiple segues with identifiers and work with them.
EDIT:
If you want to push to the same current ViewController, performSegue method would not be the optimal solution. You should instead push/present to same ViewController it self:
class ViewController: UIViewController {
var myString: String?
override func viewDidLoad() {
super.viewDidLoad()
// here we go
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sameViewController = storyboard.instantiateViewControllerWithIdentifier("ViewControllerStoryboardID") as! ViewController
sameViewController.myString = "the value of the new myString"
navigationController?.pushViewController(sameViewController, animated: true)
print(myString)
}
}
Probably, you don't want to implement it in viewDidLoad, it's just for demonstration purpose.
Make sure that the ViewController has a storyboard ID (in code snippet, I assume it is "ViewControllerStoryboardID"):
I have an app with a UIViewController that I am using to add an item to a UITableView. The user fills out a few fields, and then the data displays in the cells. I'm doing an unwind and using a function on the calling view, to retrieve the data. To create the unwind, I click dragged from the controller to Exit, and selected my function.
Now, I want to also use the same UIViewController from a different UIViewController, to permit users to edit the data that was added. When I click the accept button, it is unwinding to the UITableViewController, instead of to the view from which it was called for editing.
The code for the accept button is:
#IBAction func acceptButtonClick(sender: UIBarButtonItem) {
performSegueWithIdentifier("ReturnFromAddItem", sender: title)
}
What I want to do is this:
#IBAction func acceptButtonClick(sender: UIBarButtonItem) {
if !editingFields {
performSegueWithIdentifier("ReturnFromAddItem", sender: title)
}
else {
performSegueWithIdentifier("ReturnFromEditItem", sender: title)
}
}
It doesn't appear that I can do this though. My segue ID is ReturnFromAddItem. I don't see a way to add two unwind segues. Any idea how I might approach this? I want the same functionality that I have on the add. I want it to unwind a function on the caller.
I should elaborate a bit. I was able to add the second unwind with control click drag to Exit, but when I call it in the performSegueWithIdentifier, it does nothing. There is no error, but it does not leave the view controller and go back to the caller.
Editing to elaborate further.
The base controller is called BaseViewController. This is a UITableView. In the navigation bar is a "+" button that calls a UIViewController named AddViewController. When the "+ button is clicked, there's a segue that takes the user to AddViewController. The user fills out a few fields and clicks an Accept button. The code for the Accept button is listed above. It calls the unwind segue, and we go back to BaseViewController. When the user taps on an item in BaseViewController, it takes the user to a UIViewController named InformationController. From there, I'd like to give the user the chance to edit the data by clicking on a Settings button on the navigation bar of InformationController. This would segue to the AddViewController UIViewController, but it would use it to edit existing fields. I want to unwind and go back to InformationController when AddViewController is called from InformationController, and back to BaseViewController when AddViewController is called from BaseViewController. The problem I'm having is that even though I added an unwind segue for AddViewController to InformationController, nothing happens when I click the Accept button.
Final Edit:
I think that I have it figured out. In InformationController, I am using viewWillDisappear to call the unwind segue back to BaseController. When I click settings to call AddViewController from InformationController, viewWillDisappear is firing, and I am unwinding to BaseViewController. So what I need to figure out is whether I should continue to use viewWillDisappear and find a way to tell it that it is not disappearing because I'm done with it, or if I should be executing the unwind from somewhere else.