I want to pass indexPath.row of the clicked row to another controller, so I used the next code, but it prints the error, that view controller does not have a member named tableview.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "body"{
var secondViewController : SecondViewController = segue.destinationViewController as! SecondViewController
var indexPath = self.tableView.indexPathForSelectedRow() //get index of data for selected row
secondViewController.data = indexPath.row // get data by index and pass it to second view controller
}
}
Can I fix this error?
I think the tableView doesn't exists there. and it is not a tableView function.
You have to create an strong outlet for the tableView like this:
#IBOutlet var tableView: UITableView!
and don't forget to connect this to your tableView
and delete override from the methods TableView.
EDIT:
For your another problem you can follow Kirsteins as he suggested:
tableView.indexPathForSelectedRow method returns an optional NSIndexPath. You have to unwrap it. However the best approach would be handle the situation with safely unwrap using if let where there is no selected row and indexPath is nil. Something like:
if let indexPath = tableView.indexPathForSelectedRow() {
secondViewController.tempString = indexPath.row.description
} else {
// handle the situation where the is no selected row
}
and declare a variable into your SecondViewController which will hold this value.
Check THIS for more Info.
Related
I am trying to pass value I get from Firebase to another tableView. I get 2 values from Firebase - "Brands" and "Products". I am trying to make like car app. If you click on Ford then new tableView will appear and shows all the Ford models. This is what I've done so far.
like this I get Brands from Firebase:
func parseSnusBrands(){
let ref = FIRDatabase.database().reference().child("Snuses").child("Brands")
ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if snapshot.exists() {
if let all = (snapshot.value?.allKeys)! as? [String]{
for a in all{
if let products = snapshot.value![a] as? [[String:String]]{
self.snusBrandsArray.append(["key":a,"value":products])
}
}
self.snusBrandsTableView.reloadData()
}
}
})
}
And like this I detect which cell is clicked and print the product that belongs to the clicked Brand:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
print("products at \(indexPath.row) --> \(snusBrandsArray[indexPath.row]["value"])")
}
How to pass the (snusBrandsArray[indexPath.row]["value"]) to new tableView? I tried using segues and looking for tutorials like "How to pas value between viewControllers" but I am out of luck. Right now I have 2 tableViewController.swift files and one tableViewCustomCell.swift file. Do I need some more files?
For send data, first of all declare your variable in 2nd view controller..
var productsValue = [[String:String]]()
and in 1st viewcontroller
var valueTopass = [[String:String]]()
Than in didSelectRowAtIndexPath, take a value in one valueTopass
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("products at \(indexPath.row) --> \(snusBrandsArray[indexPath.row]["value"])")
if let products = snusBrandsArray[indexPath.row]["value"] as? [[String:String]]{
valueTopass = products
performSegueWithIdentifier("toProducts", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toProducts") {
var viewController = segue.destinationViewController as! SnusProductsTableViewController
viewController.productsValue = valueTopass
print(productValues)
}
}
You need to use Segues to pass the data forward.
To pass data from the current view controller to the next new view controller using segues, first create a segue with an identifier in the relevant storyboard. Override your current view controller's prepareForSegue method. Inside the method check for the segue you just created by its identifier. Cast the destination view controller and pass data to it by setting properties on the downcast view controller.
Setting an identifier for a segue:
Segues can be performed programatically or using button action event set in the storyboard by ctrl+drag to destination view controller.
You can call for a segue programatically, when needed, using segue identifier in the view controller:
func showDetail() {
performSegueWithIdentifier("showDetailingSegue", sender: self)
}
You can configure segue payload in the override version of prepareForSegue method. You can set required properties before destination view controller is loaded.
Swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController is the name of the second view controller and isDetailingEnabled is a public variable in that view controller.
To expand on this pattern, you can treat a public method on DetailViewController as a pseudo initializer, to help initialize any required variables. This will self document variables that need to be set on DetailViewController without having to read through it's source code. It's also a handy place to put defaults.
Swift
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Why not pass the whole dictionary with all the contents from firebase to the new VC using prepare for segue?
And then store the dict in the destinationVC data model?
That should do the trick.
In my first table view I have an array which populates the table view. The user then clicks a cell. Each cell has a different text label. I want to carry the label value from one table view to the next, but when it gets to the other table view the variable I try to set it to is nil. I am using segues and have tried to use prepareForSeguge function. I just can't get the variable to save once it's left the didSelectRowatIndexPath method.
EDIT: I used an observer to check that my variable was being set, but it isn't set until after all other functions have run. How Would I then be able to use my variable in my second tableview to populate the table?
Here is my code:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
GenreSelector = currentCell.textLabel!.text
print(GenreSelector)
performSegueWithIdentifier("letsGo", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "letsGo") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destinationViewController as! ResultTableViewController
// your new view controller should have property that will store passed value
viewController.GenreSelection = GenreSelector
}
}
I am using a UITableView with two sections, I use an array called sections for the section header titles, and an array called sectionItems which holds two arrays of string value to populate the sections.
let sections = ["Section 1", "Section 2"]
var sectionItems = [ ["One","Two", "Three", "Four"], ["Another One"] ]
The UITableView displays the data fine, however I am trying to segue when the user selects a table view cell. The problem is I have two different segues for the two different sections.
How can I use each segue for the appropriate section ?
This is what I was trying,
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
tableView.deselectRowAtIndexPath(indexPath, animated: true)
//Optional error occurs here for section property
let section = self.tableView.indexPathForSelectedRow!.section
if section == 0 {
self.performSegueWithIdentifier("sectionOneSegue", sender: cell)
} else if section == 1 {
self.performSegueWithIdentifier("sectionTwoSegue", sender: cell)
}
}
However this give me an optional error when declaring the property section.
Also when the segue is initiated I am trying to pass the relevant data to the detail controller.
Here I am trying to make sure in the sectionItems array I use the first array within the array by using the section number, then the exact string value for the selected cell row.
if segue.identifier == "sectionOneSegu" {
let navController = segue.destinationViewController as! UINavigationController
let detailController = navController.topViewController as! DetailViewController
var row = self.tableView.indexPathForSelectedRow!.row
detailController.upcomingType = sectionItems[1][row]
detailController.mode = 1
}
I am unsure if this is correct. Can someone help me out on why the error is occurring when selecting the cell row and how to fix it to use the correct segue ? And how can I pass the relevant data ?
First of all you don't need to get the cell in your 'didSelectRowAtIndexPath' method, so the first line where you create a reference to the cell is unnecessary.
You have an index path that has a row and section index within it, use that to obtain the data out of your sectionItems array e.g
let data = sectionItems[indexPath.section][indexPath.row]
switch indexPath.section {
case 0:
self.performSegueWithIdentifier("sectionOneSegue", sender: data)
case 1:
self.performSegueWithIdentifier("sectionTwoSegue", sender: data)
default:
break
}
That should be all you need within that method.
You can then override the prepareForSegue method and check the segue.identifier property to see which segue has been performed and extract the destinationViewController from there. Once you have the view controller you can pass your data to it through a property on the view controller.
if segue.identifier == "sectionOneSegue" {
guard let data = sender as? String,
newViewController = segue.destinationViewController as? NewViewController else {
return
}
newViewController.data = data
}
In the above code I'm making sure that the sender is the expected data type (the one sent from the above performSegueWithIdentifier method) and that the destination controller is the one that I want, then as soon as I know everything is correct I'm setting the property on the destination controller with the data that I want to send to it.
I hope this helps.
Since you are inside tableView:didSelectRowAtIndexPath: method, you do not need to look for index path of the selection like this:
let section = self.tableView.indexPathForSelectedRow!.section
The desired indexPath is given to you in a variable, so all you need to do is
if indexPath.section == 0 {
self.performSegueWithIdentifier("sectionOneSegue", sender: cell)
} else if indexPath.section == 1 {
self.performSegueWithIdentifier("sectionTwoSegue", sender: cell)
}
or use an array that gives you segue name for section index, like this:
private static let segueForIndex = ["sectionOneSegue", "sectionTwoSegue"]
...
self.performSegueWithIdentifier(MyClass.segueForIndex[indexPath.section], sender: cell)
I need to pass a variable containing the index of the selected row in one view to the next view.
firstview.swift
var selectedRow = 0;
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row + 1
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "viewAssignmentsSegue") {
let navController = segue.destinationViewController as! UINavigationController
let controller = navController.viewControllers[0] as! AssignmentsViewController
print(selectedRow)
controller.activeCourse = selectedRow
}
}
My issue is that, when a row is selected, the selectedRow variable isn't updated by the tableView method before the segue occurs, meaning that the value is essentially always one behind what it should be. How can I delay the prepareForSegue until the variable is updated or how else can I successfully pass the selected row to the next view without delay?
One possibility: Don't implement didSelectRowAtIndexPath:. Just move that functionality into your prepareForSegue implementation. That, after all, is what is called first in response to your tapping the cell. Even in prepareForSegue you can ask the table view what row is selected.
Another possibility: Implement willSelectRowAtIndexPath: instead of didSelectRowAtIndexPath:. It happens earlier.
in your prepareForSegue:
controller.activeCourse = self.tableView.indexPathForSelectedRow
don't wait for didSelect to be called - react earlier.
Use this on prepareForSegue:
let path = self.tableView.indexPathForSelectedRow()!
controller.activeCourse = path.row
or
let path = self.tableView.indexPathForCell(sender)
controller.activeCourse = path.row
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!