I've been trying to pass data to another View Controller. But as I have two Bar Buttons that lead to two different View Controllers I have to set a condition. But when I try to pass the data it won't work. Here's the code I've been using:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ViewController") {
let destViewController = segue.destinationViewController as! ViewController
destViewController.chips = chips
destViewController.blind = blind
}
}
I have my destination Storyboard id to ViewController but when ViewController view opens the data isn't passed through.
EDIT:
In both View Controllers chips and blind are declared as:
var chips = 0
var blind = 0
Before I added a back button the data was passed correctly. But then the application crashed every time I clicked "Back" so I decided to add a condition which doesn't seem to work.
I'm very new to Xcode/Swift, but I believe the string in your if (segue.identifier == "ViewController") is the problem. Instead of "ViewController" you need to use the identifier of your segue. Select your segue in Main.storyboard, click on the Attributes Inspector and give it a name in the Identifier field. That's the string you want to use.
Related
This question already has answers here:
Changing label text of second ViewController upon clicking button in first ViewController
(4 answers)
Closed 6 years ago.
When using the prepare (forSegue) method to pass data to the destination view controller of a segue, all members of the destination VC are nil. Thus, my program crashes when trying to access these members.
My code in the source view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Second2DetailEdit"{
var detailVC = segue.destination as! DrinkDetailViewController
detailVC.headerLabel.text = "Edit Drink"
}
//...
}
The VC itself is not nil. Only all members of the VC. Did I forget something?
This does only occur in my new Swift 3 project. Similar code in my Swift 2 projects have no problems.
Thanks in advance.
The destination view controller's view is not created yet and that's why all IBOutlets are nil. Try sending the information you need in normal properties like Strings and in ViewDidLoad of the destination, update your view.
The best way around this is to create a variable in your second view controller and set the text to that instead.
Then once the vc has initialized you can set the headerLabel.text to the new variable in viewDidLoad
I would suggest safely unwrapping your detailVC rather than force unwrapping...
if segue.identifier == "Second2DetailEdit" {
if let detailVC = segue.destination as? DrinkDetailViewController {
detailVC.headerLabel.text = "Edit Drink"
}
}
You'll have an easier time setting properties on the destinationVC and passing info to those rather than trying to do it through UILabels...
Up to date Xcode/Swift/iOS.
I have a Master VC (called StartVC) that contains a Child VC (called TopBarVC) via and embedded segue. The Child VC contains a button, that, when pressed, modally segues to a 3rd VC (called CategoryPickerOverlayVC) (the view in this VC serves as a dropdown box for picking a category).
#IBAction func CategoryFilterButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toCategoryPickerOverlay", sender: self)
}
When an option is selected from the dropdown box, which itself is composed of three buttons, the title of the selected button should be used to replace the title text of the button in the Child VC.
In the Master VC, I use prepareforsegue to store a reference to the Child VC in a variable - "topBarReference" - at the moment when the embed segue takes place.
var topBarReference: TopBarVC?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TopBarPane"{
topBarReference = segue.destination as? TopBarVC
}
}
Then, in the 3rd VC, when I click on one of the button options in the dropdown box, the button title is sent via a prepareforsegue to update the button in the Child VC (via "topBarReference").
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.filterButtonText = ((sender as! UIButton).titleLabel?.text)!
}
The 3rd VC then unwind segues back to the Master VC. I should add that when the button in the Child VC is changed, a variable (filterButtonText) in Child VC is first set with the title text and then this variable is then used to set the button title text via the viewDidAppear method of Child VC.
When using the debugger, I also note that viewDidAppear in the Master VC does not seem to execute after unwinding (I placed a diagnostic print-to-console in viewDidAppear and nothing prints after the unwind segue). I realise this would explain the button not getting updated but I've got no idea why viewDidAppear does not run.
I have also tried using a delegate protocol and instantiateViewController(withString:) to no avail. All of the methods produce the same result, which is that the button in the Child VC does not get updated. No errors are shown. Everything else happens as expected.
Any ideas as to what I am doing wrong?
Do you mean something like this?
If so, the solution I used was very simple: the third VC uses prepareForSegue to set a property of the embedded VC, and the embedded VC picks up that property in the unwind method.
In my implementation, the three view controllers are called ViewController, ChildViewController, and ThirdViewController. This is the entire code (everything else is configured in the storyboard):
class ChildViewController: UIViewController {
#IBOutlet weak var theButton: UIButton!
var buttonTitle : String?
#IBAction func unwind(_:UIStoryboardSegue) {
self.theButton.setTitle(self.buttonTitle, for: .normal)
}
}
class ThirdViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as! ChildViewController).buttonTitle = (sender as! UIButton).currentTitle
}
}
Ok, so I have found that my original code works fine bar one line in the prepareforsegue of the Child VC. If I change that prepareforsegue from:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.titleLabel?.text = ((sender as! UIButton).titleLabel?.text)!
}
to this:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.setTitle((sender as! UIButton).titleLabel?.text, for: .normal)
}
it works just fine. The use of the .setTitle method seems to make a difference although I am not sure why.
Thanks to Matt for giving me the idea to change it to that. Matt's method did work when i tried it, although, as I am unwinding to the Master VC and not the Child VC, I had to edit the code accordingly, in terms of where I placed it.
As my little "discovery" equates to the smallest change to the original code, I'll mark this as the answer.
Thanks to all for taking the time to respond!
I have a storyboard with two main view controllers (ViewController / SecondViewController) and a container with an embedSegue that displays the EmbeddedViewController view.
Desired Result
I would like to pass the 5 from ViewController to EmbeddedViewController using prepareForSegue (I don't see a need for delegation here, since the data is only going 1 way, and that's TO the EmbeddedViewController.) Once "Segue" button is pressed, the second view controller appears with the yellow VC embedded with 5 in it.
Note - SecondViewController has no user interaction in displaying EmbeddedViewController. It just shows up automatically.
Problem
I can't get a reference to EmbeddedViewController from the mainSegue prepareForSegue. Nor can I access any child View Controllers from SecondViewController. Thought I could access properties of EmbeddedViewController if I access the children VC's of SecondViewController. See code below.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mainSegue" {
let secondVCSegue = segue.destinationViewController as! SecondViewController
print("array -- \(secondVCSegue.childViewControllers)")
// Array prints nothing. Just a blank [] :(
}
}
Any tips are appreciated thanks.
I promise that I'm completely new to Xcode and Swift, so I know I am making silly mistakes but I don't know where. This is part of my iOS app storyboard:
where the segue between the first table view and the second navigation controller is called myTaskDetailSegue and its type is Show (e.g. Push). Now I have some problems:
Neither in the first table view controller nor in the second the back button is showed and I don't know why. Many people told me that navigation bar and back button are as default in navigation controllers but they did not appear
In the class of the first table view controller here is the method prepareForSegue()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "myTaskDetailSegue" ) {
let indexPath = self.tableView.indexPathForSelectedRow()
let task = self.taskCollection[indexPath!.row] as Task
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailsMyTasksViewController
controller.detailItem = task
println("segue mostra task \(task.id)")
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
so you can read that the segue identifier is correct but when a row is tapped nothing happens and the second table view controller is not showed.
I don't really know what I am missing because of my inexperience.
Here is the complete storyboard:
You don't need two UINavigationController's to what you want to achieve. Is important to note that every time you push(with a segue or manually) a new UIViewController it's added to the navigation stack.
According to Apple:
Pushing a view controller displays its view in the navigation interface and updates the navigation controls accordingly. You typically push a view controller in response to user actions in the current view controller—for example, in response to the user tapping a row in a table.
So you can remove the second UINavigationController in your Storyboard and make the segue directly to your DetailsMyTaskViewController and update your prepareForSegue like in the following way:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "myTaskDetailSegue" ) {
let indexPath = self.tableView.indexPathForSelectedRow()
let task = self.taskCollection[indexPath!.row] as Task
let controller = segue.destinationViewController as! DetailsMyTasksViewController
controller.detailItem = task
println("segue mostra task \(task.id)")
}
}
And your back button should appear by default as you said before. Nevertheless I strongly recommend you read the following two guides :
UINavigationViewController
View Controller Programming Guide for iOS
For a better understanding of the navigation stack, etc.
I hope this help you.
I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?
Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}
It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.