Im trying to pass data from viewController 1 to viewController2, I have 2 buttons and 1 segue(therefore there is one segue identifier) for those 2 buttons, each button when pressed should show: 1 label to show the title and 1 textView to show a definition, I am having troubles to show its own data of each word; I know it has to be the some code referencing the SENDER in the performSegueWithIdentifier, but I don't know how to do it.
I appreciate your help !!! thanks.
here is my code
class ViewController: UIViewController {
#IBAction func AbstractionBtn(sender: AnyObject) {
performSegueWithIdentifier("ShowDefinition", sender: "Abstraction")
}
#IBAction func binarySystemBtn(sender: AnyObject) {
performSegueWithIdentifier("ShowDefinition", sender: "Binary System")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ShowDefinition") {
if let destinationViewController = segue.destinationViewController as? EnglishViewController {
destinationViewController.titleMsg = "Abstraction"
destinationViewController.definitionMsg = "Abstraction Definition"
}
} else if(segue.identifier == "ShowDefinition"){if let destinationViewController = segue.destinationViewController as? EnglishViewController {
destinationViewController.titleMsg = "Binary System"
destinationViewController.definitionMsg = "Binary System Definition"
}
}
}
You have correctly passed the definition as a String in the sender parameter in performSegueWithIdentifier. You just need to use its value in prepareForSegue, but first you must cast it from AnyObject? back to a String.
Your code could be something like:
class ViewController: UIViewController {
#IBAction func AbstractionBtn(sender: AnyObject) {
performSegueWithIdentifier("ShowDefinition", sender: "Abstraction")
}
#IBAction func binarySystemBtn(sender: AnyObject) {
performSegueWithIdentifier("ShowDefinition", sender: "Binary System")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ShowDefinition") {
if let destinationViewController = segue.destinationViewController as? EnglishViewController {
if let definition = sender as? String {
if definition == "Abstraction" {
destinationViewController.titleMsg = "Abstraction"
destinationViewController.definitionMsg = "Abstraction Definition"
} else if definition == "Binary System" {
destinationViewController.titleMsg = "Binary System"
destinationViewController.definitionMsg = "Binary System Definition"
}
}
}
}
}
}
Try this one
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "ShowDefinition")
{
if let destinationViewController = segue.destinationViewController as? EnglishViewController
{
var btn = sender as! UIButton
if btn.tag == 1
{
destinationViewController.titleMsg = "Abstraction"
destinationViewController.definitionMsg = "Abstraction Definition"
}
else
{
destinationViewController.titleMsg = "Binary System"
destinationViewController.definitionMsg = "Binary System Definition"
}
}
}
}
And set button tag like this
AbstractionBtn.tag = 1
binarySystemBtn.tag = 2
Now Call for segue like this
#IBAction func AbstractionBtn(sender: AnyObject) {
performSegueWithIdentifier("ShowDefinition", sender:sender)
}
Create a var named titleMsg and definitionMsg.
In each #IBAction method set the appropriate title message to self.titleMsg and similarly for self.definitionMsg.
After that call,
performSegueWithIdentifier("ShowDefinition", sender: self)
And then override,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDefinition" {
let destinationVC = segue.destinationViewController as? EnglishViewController
destinationVC.titleMsg = self.titleMsg
destinationVC.definitionMsg = self.definitionMsg
}
}
Hope that helped!
Related
I've got problem with some additional challenges. I need to filter an array of type Question by some property and then pass it into next View Controller via segue. I've done this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let sender = sender as? UIButton else {return}
if sender == quiz3Button {
let vc = segue.destination as? QuestionViewController
vc?.correctQuestions = questions.filter { question in
return question.quiz == .animals
}
} else if sender == quiz4Button {
let vc = segue.destination as? QuestionViewController
vc?.correctQuestions = questions.filter { question in
return question.quiz == .cars
}
}
}
#IBAction func quiz3ButtonTapped(_ sender: UIButton) {
performSegue(withIdentifier: "animals", sender: sender)
}
#IBAction func quiz4Button(_ sender: UIButton) {
performSegue(withIdentifier: "cars", sender: sender)
}
Filtration works but it doesn't pass value to next View Controller. I declared variable in QuestionViewControler like that
var correctQuestions: [Question] = []
But when I need to access it I get error "Index out of range". So I figured that its empty..
Segues been made from buttons to VC
Ok. I've got it. The NavigationController was the problem here. Added into function push through NC and it worked ;) so closed I think
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let sender = sender as? UIButton else {return}
if sender == quiz3Button {
let destinationViewController = segue.destination as? UINavigationController
let questionViewController = destinationViewController?.viewControllers.first as! QuestionViewController
questionViewController.correctQuestions = questions.filter { questions in
return questions.quiz == .animals
}
} else if sender == quiz4Button {
let destinationViewController = segue.destination as? UINavigationController
let questionViewController = destinationViewController?.viewControllers.first as! QuestionViewController
questionViewController.correctQuestions = questions.filter { questions in
return questions.quiz == .cars
}
}
}
So here is my code from VC1 and passing the data to VC2.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedArtist = artists[indexPath.item]
performSegue(withIdentifier: "artistToArtSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController()
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
These codes here in VC2 will print the data
class ArtCollectionController: UICollectionViewController {
var artist = Artist() {
didSet{
print(artist.artistId ?? "did not work")
print(artist.name ?? "what name?")
}
}
var selectedArtist = Artist()
but when I use the the variable in these following test codes in VC2. They return a nil.
func fetchArtForArtist() {
guard let artistId = selectedArtist.artistId else {return}
print(artistId)
let fanRef = FIRDatabase.database().reference().child("art_ref").child(artistId)
fanRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
let artId = snapshot.key
print(artId)
// let dataRef = FIRDatabase.database().reference().child(artId)
// dataRef.observe(.value, with: { (snapshot) in
// let dictionary = snapshot.value as? [String: AnyObject]
// //let art =
// }, withCancel: nil)
}, withCancel: nil)
}
#IBAction func testButton(_ sender: UIBarButtonItem) {
print(selectedArtist.name ?? "no name")
print(12345)
}
override func viewDidAppear(_ animated: Bool) {
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
override func viewDidLoad() {
super.viewDidLoad()
fetchArtForArtist()
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
Im doing this in storyBoard. Im even using 2 vars seeing if there is a difference. I dont understand why the data is successfully passed to VC2 to a couple variables but when the variable is used it returns a nil. Please help.
The other responses are good, but I prefer a slightly different approach:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let artCollectionController as ArtCollectionController:
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
case let otherViewController as OtherViewController:
//Code for some other case
}
}
By using a switch statement, you have a prepareForSegue that will handle multiple different segues cleanly.
The case let construct is a cool trick that only executes that case if the variable in the switch can be case to the desired type. If it can be cast, it creates a local variable of the desired type.
I prefer deciding what code to execute based on the class of the destination view controller because it's less fragile than using the segue identifier. If you forget to set the segue identifier, or add a second segue later to the same type of view controller, or have a typo in the name of the identifier, that code won't work. If you have a typo in your class name, though, the compiler throws an error.
Because you set the artist property on a new instance of ArtCollectionController, which is destroyed upon the exit of the prepareForSegue function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController() // created
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
// destroyed here
}
}
Try this instead:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue",
let artCollectionController = segue.destination as? ArtCollectionController
{
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = segue.destination as! ArtCollectionController
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
try this, you are creating one more ArtCollectionController instead of passing data to segue one
I have marked the part of my code where the problem is, it is commented out. The error message is:
Cannot assign value of type String! to type UILabel!.
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.messageLabelDos = sendingText
}
}
}
The label it should be changing is in my DetailViewController and it is a label. The code above is from my original ViewController. How can I make this work?
More code to put in context:
if metadataObj.stringValue != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("SendDataSegue", sender: self)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.viaSegue = sendingText
}
}
}
You need to pass the String instead of setting text to label, because when you correct it and set like this sendToDetailViewController.messageLabelDos.text = sendingText, you will get nil error because messageLabelDos is not initialize yet, so try like this. Create one string instance in DetailViewController and use that inside prepareForSegue for passing String and then use that String instance in viewDidLoad to assign Label to text.
class ViewController: UIViewController {
//Your other methods
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SendDataSegue" {
if let sendToDetailViewController = segue.destinationViewController as? DetailViewController {
var sendingText = metadataObj.stringValue
sendToDetailViewController.messageDos = sendingText
}
}
}
}
Inside DetailViewController
var messageDos: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.messageLabelDos.text = messageDos
}
I have to functions, both of them trigger performSegueWithIdentifier with the same segue. But depending of which function was called I need to pass different parameters in prepareForSegue.
Some thing like
func first() {
// do some stuff
performSegueWithIdentifier("My segue", sender:AnyObject?)
}
func second() {
// do some stuff
performSegueWithIdentifier("My segue", sender:AnyObject?)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "My segue" {
let destination = segue.destinationViewController as! MyController
if functionFirstWasCalled {
destination.property = value
} else if functionSecondWasCalled {
destination.property = anotherValue
}
}
}
Surely, I can do this by setting booleans from second() and first() and then checking them in prepareForSegue - but maybe there is some more elegant way to do this ?
In objective -c you would do:
[self performSegueWithIdentifier:#"segueNAme" sender:#"firstMethod"];
and you can access this message in the prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([sender isEqualToString:#"firstMethod"]) {
//firstMEthod called the segue
}
}
The swift equivalent I think would be:
self.performSegueWithIdentifier("segueNAme", sender: "firstMethod")
and
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject) {
if (sender == "firstMethod") {
//firstMEthod called the segue
}
}
My suggestion would be to instead of sending a plain string , send a dictionary type object that contains the methodName, className and some other params useful for future debugging.
All you have to do is send a flag by sender attribute, something like this:
func first() {
performSegueWithIdentifier("My segue", sender:true)
}
func second() {
// do some stuff
performSegueWithIdentifier("My segue", sender:false)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "My segue" {
let destination = segue.destinationViewController as! MyController
let isFirstFunctionCalled = sender as! Bool // cast sender to bool
if isFirstFunctionCalled {
destination.property = value
} else {
destination.property = anotherValue
}
}
}
Simply set parallel properties in this View Controller which you then pass to the destination View Controller based on the function called. i.e:
var a = ""
var b = 0
func first() {
// do some stuff
a = "first function determined this variable."
b = 1
performSegueWithIdentifier("My segue", sender:AnyObject?)
}
func second() {
// do some stuff
a = "second function determined this variable."
b = 182
performSegueWithIdentifier("My segue", sender:AnyObject?)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "My segue" {
let destination = segue.destinationViewController as! MyController
if functionFirstWasCalled {
destination.property = a
} else if functionSecondWasCalled {
destination.property = b
}
}
}
I have this prepareForSegue in my first ViewController with a enum in it
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
enum TypeOfSegue {
case Edit, Create
}
if let DestViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "EditItem" {
let edit = TypeOfSegue.Edit
}
if let DestViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "CreateNewItem" {
let create = TypeOfSegue.Create
}
}
I want to pass the constants edit or create to my DetailViewController. If possible, how can I do this and under what variable will the TypeOfSegue be saved in DetailViewController?
You can define a mode property in your DetailViewController
class DetailViewController:UIViewController {
var mode: TypeOfSegue!
}
Next you can populate it
enum TypeOfSegue {
case Edit, Create
}
class ListViewController:UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "EditItem" {
destViewController.mode = .Edit
}
if let destViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "CreateNewItem" {
destViewController.mode = .Create
}
}
}
Update
You can also implement the prepareForSegue method this way
enum TypeOfSegue: String, CustomStringConvertible {
case Edit = "EditItem", Create = "CreateNewItem"
var description: String { return self.rawValue }
}
class ListViewController:UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let
destViewController = segue.destinationViewController as? DetailViewController,
identifier = segue.identifier,
segueType = TypeOfSegue(rawValue: identifier) else { fatalError("Wrong segue") }
destViewController.mode = segueType
}
}