How to access segue in 'didSelectRowAtIndexPath'? - ios

I know how to pass data using segues from prepareForSegue function, but the thing i have a TableViewCell from which there are two possible segues to two different ViewControllers (let's just say A, B as of now).
It was suggested here that it is best to connect the segues to View controller rather than the tableCell itself, which infact worked perfectly. But i want to pass information to the second View controller when the cell is clicked, So how to access segue that i connected to the Source ViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showQuestionnaire"{
if let indexPath = self.tableView.indexPathForSelectedRow() {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! QuestionnaireController
let patientQuestionnaire = patientQuestionnaires[indexPath.row] as! PatientQuestionnaire
controller.selectedQuestionnaire = patientQuestionnaire
self.performSegueWithIdentifier("showQuestionnaire", sender: self)
}
}
}
Again: This question is not regarding passing information through prepareForSegue

You should be using the didSelectRowAtIndexPath method to determine whether or not a cell was selected, and send the indexPath as the sender of the segue:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showQuestionnaire", sender: indexPath);
}
Then in your prepareForSegue method, get the indexPath from the sender parameter and use that to pass the correct row/data:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "showQuestionnaire") {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! QuestionnaireController
let row = (sender as! NSIndexPath).row; //we know that sender is an NSIndexPath here.
let patientQuestionnaire = patientQuestionnaires[row] as! PatientQuestionnaire
controller.selectedQuestionnaire = patientQuestionnaire
}
}
To explain...
I used the index path as the sender so I can easily pass the index path. You could also check for the currently selected cell using other UITableView methods, but I've always done it this way with success
You can't put performSegueWithIdentifier within the prepare for segue method, because performSegueWithIdentifier leads to prepareForSegue; You are just looping around and around with no aim. (When you want to perform a segue, prepareForSegue is always executed)
prepareForSegue doesn't run by itself when a row is selected. This is where you need didSelectRowAtIndexPath. You need a performSegueWithIdentifier outside of the method as described previously, which should be in didSelectRowAtIndexPath

Related

Passing Core Data objects to another view controller when selecting row from UITableView

EDIT based on provided answer:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedName = quizNames[indexPath.row]
performSegueWithIdentifier("saveSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "saveSegue" {
let inputController = segue.destinationViewController as? QAInputViewController
inputController?.quizName = selectedName
}
}
I have done some research on here trying to figure out exactly how to use prepareForSegue in order to pass the selected Core Data object from a UITableView cell to a second View Controller, but what i found and tried to implement did not work out for me as it seems some of the code might be outdated. Here is what i am trying to do:
I populate a UITableView with an array of names of type Name which is my NSManagedObject. When i select a specific name it will segue me to an input screen where a question and answer will be submitted. I want to make sure that the question and answer that are submitted stay assigned to the previously selected name. I am not sure how to set this up in prepareForSegue. I did set up my data model with relationships, so if i did that correctly it should work once i understand how to implement it. Is there some way i can check the currently selected name so that i know any question and answer that is inputted will be saved to that name?
Relative to my above question, i want the title of the second View Controller to show the name of the previously selected name.
Basically i am just looking for the guidance on to get this implemented in Swift 2. Thank you very much for your help.
EDIT: Passing Core Data objects from UITableViewCell to another View Controller
The above link is the solution i tried implementing that was not working for me. Specifically:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let thisTask = fetchedResultsController.objectAtIndexPath(indexPath!) as! TaskModel
detailVC.detailTaskModel = thisTask
detailVC.delegate = self
}
This is how I do it:
At the top of my class I'll have a var that stores the selected item:
var selectedTask: Task?
Then in my tableViews didSelectRow method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedTask = tasks[indexPath.row]
performSegueWithIdentifier("showTaskDetail", sender: self)
}
And finally:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
detailVC.detailTaskModel = selectedTask
detailVC.delegate = self
}
}
However, I'd be more inclined to just pass some kind of ID/name and perform another fetch with a new moc rather than pass around core data objects.
You'll get less threading issues this way and it will make things far easier should you need to deep link into your app, from say, a notification or something.
Mocs are cheap, use them once, and throw them away.

Pass Data from UICollectionView in Swift 2

My problem is I would like to Pass data from my collection view to a new view (tableview- but I think it's not necessary info)
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = DetailsTableViewController()
myVC.movieTitle = movies[indexPath.row].movieTitle!
print (movies[indexPath.row].movieTitle!)
performSegueWithIdentifier("DetailSegue", sender: reuseIdentifier)
}
Print is OK, but my movieTitle: String? will NIL on the DetailsViewController.
Help!
performSegueWithIdentifier will instantiate an appropriate destination controller and present it. The DetailsTableViewController you have instantiated here as myVC is never shown. You have a couple of options for how to approach this:
If you want to use segues then trigger the segue in didSelectItemAtIndexPath and implement prepareForSegue to pass data to the destination view controller (a destinationViewController will be available on the UIStoryboardSegue passed to that method).
Alternately you could avoid using segues and present your myVC directly by pushing it onto your current navigation controller (if one exists you could call self.navigationController.pushViewController(myVC, animated:true)), presenting it as a modal, or whatever makes sense in your app.
problem solved. thanks
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "DetailSegue" {
let detailsVC = segue.destinationViewController as! DetailsTableViewController
if let cell = sender as? UICollectionViewCell, indexPath = collectionView!.indexPathForCell(cell) {
// use indexPath
detailsVC.movieTitle = movies[indexPath.row].movieTitle!
}
}
}

Passing variable (Int) to a View Control (Popover)

I have a tableView with 7 rows, each row is a different action. When a user taps on a row, a view controller is shown as a popover and a text field is displayed for them to add a note. I then want to save this note to Parse but I need to save the passedChildID with it and therefore it (passedChildID) needs to be passed from the tableviewcontroller. I have this working in other area's of my app from one tableView to another tableView but for some reason it won't work with the popover.
The following code is in SingleChildViewTableViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "childToNote" {
let popoverViewController = segue.destinationViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
let noteActionViewController = segue.destinationViewController as! actionNoteViewController
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {
noteActionViewController.passedChildID = passedChildID
}
}
if segue.identifier == "childToItemNeeded" {
//other stuff
}
}
This builds and runs fine, but when I tap on that particular row, I get the following error.
Could not cast value of type '<<app_name>>.SingleChildTableViewController' (0x10e1bef60) to 'UITableViewCell' (0x110bca128).
I have tried moving the let noteActionViewController... code above the let popoverViewController figuring that the former was never getting run but that didn't change anything.
The segue is being performed as followed (in case it makes any difference).
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
performSegueWithIdentifier("childToNote", sender: self)
}
}
I'm stumped because this code has worked elsewhere in my app without fail.
it looks like your problem lies in this line
indexPathForCell(sender as! UITableViewCell)
the sender in this case you are telling it to be self, which is a UIViewController
performSegueWithIdentifier("childToNote", sender: self)
You are then telling in the indexPathForCell line to cast it as a UITableViewCell but its of type SingleChildTableViewController. So change the sender to be the UITableViewCell
Maybe you can save the indexPath as a local variable and when you select a cell you change the value of the variable.

Send variable value to next view controller when click a table cell

I have two table view controllers
InvoiceList view controller
InvoiceShow view controller
I use didSelectRowAtIndexPath method as bellow to get selected table cell specific value
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let rowObject = objects[indexPath.row]
let invoicehash = rowObject["hash_key"]!
}
i need to send invoicehash value to InvoiceShow controller when click the table cell of InvoiceList
i tried to use prepareForSegue function. but it is not applicable because it will trigger before the didSelectRawAtIndexPath function. so when i implemented it, gives the previous click event variable value. not correct one.
Please help me to access invoiceHash variable value from InvoiceShow controller
You will get the selected cell in prepareForSegue method itself.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let selectedIndexPath = self.tableView.indexPathForSelectedRow()!
let rowObject = objects[selectedIndexPath.row]
let invoiceHash = rowObject["hash_key"]!
let invoiceShowViewController = segue.destinationViewController as! InvoiceShowViewController
// Set invoiceHash to `InvoiceShowViewController ` here
invoiceShowViewController.invoiceHash = invoiceHash
}
You can still use a segue if you want and/or already setup on your storyboard.
You just need to connect the two view controllers in Interface Builder directly from one to another.
So, start ctrl-dragging from the controller itself and not from the TableViewCell (take a look at the screenshot)
then use the performSegueMethod with the new segue identifier like this:
self.performSegueWithIdentifier("mySegueIdentifier", sender: self)
and finally, your prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegueIdentifier" {
let selectedIndex = self.invoiceTableView.indexPathForSelectedRow
//if element exist
if selectedIndex?.row < myDataSourceArray.count {
let destination = segue.destinationViewController as! InvoiceShowViewController
let invoice = myDataSourceArray[selectedIndex!.row]
destination.invoice = invoice
}
}
}
That's it!

Swift indexpath = optional(0)

i am trying to build my first iOS app with swift. I am stuck at a segue where i want to give the row indexpath to the next page, but the indexpath is "optional(0)" instead of "0". does anyone know why? in the code, there's a print sender. That one says "optional(0)" The variable "viewController.passedValue" is set to 0, the real code is behind it in comments
does anyone know why it gives "Optional(0)"?
Code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController = segue.destinationViewController as! NewsDetailViewController
print(String(sender))
viewController.passedValue = 0 // sender as! int
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("DetailSegue", sender: indexPath.row)
print("HIER")
}
You are calling
performSegueWithIdentifier("DetailSegue", sender: indexPath.row)
And then, iOS is calling your prepareForSegue, in which sender is defined as an AnyObject?, i.e. an optional AnyObject:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
...
}
Thus, when you print sender, it's an optional and thus informs you of such. If you don't want the optional, you could unwrap it (either forced unwrapping or, better, optional binding).
--
As an aside, though, it is a misuse of sender to be storing the indexPath.row there. As the documentation for performSegueWithIdentifier says, the sender is:
The object that you want to use to initiate the segue. This object is made available for informational purposes during the actual segue.
But indexPath.row is not the object that initiated the segue (the table view cell is). I therefore would advise against using the sender to hold the indexPath.row. A more common pattern would be
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? NewsDetailViewController, let row = tableView.indexPathForSelectedRow?.row {
destination.passedValue = row
}
}
This also has the virtue of giving you the alternative of not defining a didSelectRowAtIndexPath at all, and just represent your segue from the storyboard's cell prototype directly to the NewsDetailViewController scene. If you really want to segue programmatically, you can do that, but it's not necessary. But I would not advise using sender to represent the indexPath.row, but rather the UI control that triggered the segue.

Resources