First of all I would like to describe what actually I am trying to do.
I have two tableViewControllers - first one contains a list of bill's numbers and second one should displays the detail information about the bill depending on which cell of bill's number were selected.
In my first tableViewController I am using this to get bill's number from the cell and pass it to detailTableViewController:
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!) as! CustomBillTableViewCell
valueToPass = currentCell.billNumberLabel.text
print(valueToPass)
performSegue(withIdentifier: "billsDetailSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "billsDetailSegue"){
let detailController = segue.destination as! BillsDetailTableViewController
detailController.receivedValue = valueToPass
}
}
The problem is that didSelectRowAt function doesn't get triggered at all, after cell selection in the first tableView only prepare function triggers and It passes an empty value to the next view.
you override didDeselect instead of didSelectRowAt.
And you probably set a segue for tapping on cell.
Disconnect it and connect segue from your whole viewController.
Maybe you set your UITableView's selection styles to non-clickable.
Make UITableView -> Selection -> Single Selection and UITableViewCell -> Selection -> Default or something except the None
Make sure that you have set the delegate properly
I have a UIViewController which should show me DetailInformations depending on what Cell was pressed in the UITableViewController.
For the moment I am passing them through a sequel:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
ctrl.information = _informationList[id]
}
}
The id variable is set through:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
}
Now in my UIViewController I change the information with:
override func viewDidLoad() {
super.viewDidLoad()
setInformation(i: information)
}
Now my problem is, that if I press, lets say cell 2. It switches to the ViewController and shows Information of cell 1. Than I go back to the tableview and I press cell 3. Then it shows me cell 2.
In short, it seems that the viewController is loaded (with the last information), before it sets the new information.
Is there any better way to solve this?
Try using indexPathForSelectedRow in prepareForSegue as of it looks like that you have created segue from UITableViewCell to the Destination ViewController so that prepareForSegue will call before the didSelectRowAt.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
ctrl.information = _informationList[indexPath.row]
}
}
}
I am assuming based on what you are describing is that you used a segue in your Storyboard to link directly from the cell to the detail view controller. This is not what you want to do, as mentioned earlier, because you don't get the order of events you would expect. You could use the delegation design pattern for this, but assuming you want to stick with segues you need to make the "show" segue from the table VC itself to the detail VC. You then manually call the segue from the tableView didSelectRowAt code.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
performSegue(withIdentifier: "show", sender: self)
}
Finally, you could then use an unwind segue when you come back to catch any data changes initiated in the detail VC.
override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath : NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier ("ChecklistItem") as UITableViewCell
let label = cell.viewWithTag(1000) as UILabel
if indexPath.row == 0 {
label.text = "jaswanth"
} else if indexPath.row == 1 {
label.text = "nikhil"
} else if indexPath.row == 2 {
label.text = "krishna"
}
return cell
}
I have UITableViewController, I have used above code to display the cells in the table and I had another UIViewController when user taps on jaswanth the text(labels text) in UIViewController have be changed and the same way when nikhil is tapped the text(labels text) in UIViewController have changed
I had only one UITableViewController and one UIViewController how can I do this?
You would want to be able to detect when these cells in the table are pressed. This can be done using the recommendation by Mike Welsh through implementing the method: func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
This allows you to be able to tell which cell is selected from the NSIndexPath.
From here on, it seems to me that you want to be able to change the text (on a button or field) that is controlled by another UIViewController. One way it can be done is to have a reference to this UIViewController and creating a method to update this text field.
If the UITableViewController is a child view controller, it can be as simple as calling
let parent = parentViewController!
Else if the Controllers are related by a Segue, it can also be done by overriding prepareForSegue of the original ViewController.
By using the segue.identifier to map to the segue you have made in StoryBoard/programmatically, you are able to set a delegate/pointer to the original UIViewController.
override func prepareForSegue(_ segue: UIStoryboardSegue, sender sender: AnyObject?) {
if segue.identifier == "SomeStringID" {
self.tableViewController = segue.destinationViewController as? UITableViewController
self.tableViewController.delegate = self
}
}
With the reference to the other ViewController, you should be able to define your own method to change this text field quite easily
Hope this helps you!
I'm encountering problems with my UITableViewCells. I connected my UITableView to a API to populate my cells.
Then I've created a function which grabs the indexPath.row to identify which JSON-object inside the array that should be sent to the RestaurantViewController.
Link to my Xcode Project for easier debugging and problem-solving
Here's how my small snippet looks for setting the "row-clicks" to a global variable.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
i = indexPath.row
}
And here's my prepareForSegue() function that should hook up my push-segue to the RestaurantViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
And here's how I've set up my segue from the UITableViewCell
Here's my result, I've tried to click every single one of these cells but I won't be pushed to another viewController...I also don't get an error. What is wrong here?
Tried solutions that won't work
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let vc = segue.destinationViewController as RestaurantViewController
//let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
The problem is that you're not handling your data correctly.
If you look into your currentResponse Array, you'll see that it holds NSDictionaries but in your prepareForSegue you try to cast a NSDictionary to a NSArray, which will make the app crash.
Change the data variable in RestaurantViewController to a NSDictionary and change your prepareForSegue to pass a a NSDictionary
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = redditListTableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
For Swift 5
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = self.tableView.indexPath(for: cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destination as! RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
The following steps should fix your problem. If not, please let me know.
Remove your tableView(tableView, didSelectRowAtIndexPath:) implementation.
Make data on RestaurantViewController have type NSDictionary!
Determine the selected row in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = tableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
Dropbox link to stack3 directory
I am having difficulty understanding why your software is much different than a standard 2 level tableview structure. So I coded a short example which you can access from this link. I have also included the sources code below.
The program mimics what you have (as best as I understood it). Table Controller 1 segues to Table Controller 2 from the tableview cell. I had no issues with segue-ing. Notice that I do not have nor need to augment the Storybook to initiate the segue.
I have embedded both the controllers in Navigation Controllers. My experience is that it saves a lot of effort to set up the navigation.
Alternately, I could have control-dragged from the first TableViewController symbol on top of the screen to the second controller and set up the segue.
I used a global variable (selectedRow) although it is not a recommend practice. But you just as easily use the prepareForSegue to set a variable in the RestaurantTableViewController (I show an example)
Finally, I recommend checking the Connections Inspector (for the table view cell in the first controller) to confirm that there is a segue to the second controller. If you control-dragged properly there should be confirmation prompt as well as an entry in the Connections Inspector.
Unfortunately I just cant get the code properly formatter
import UIKit
var selectedRow = -1
class TableViewController: UITableViewController {
var firstArray = ["Item1","Item2","Item3","Item4"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return firstArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = firstArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as RestaurantTableViewController
// can write to variables in RestaurantTableViewController if required
vc.someVariable = selectedRow
}
}
import UIKit
class RestaurantTableViewController: UITableViewController {
var secondArray = ["Item 2.1", "Item 2.2", "Item 2.3", "Item 2.4"]
var someVariable = -1
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return secondArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = secondArray[indexPath.row]
if indexPath.row == selectedRow {
cell.textLabel!.text = cell.textLabel!.text! + " SELECTED"
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
}
I noticed that in your screenshot of your storyboard, the segue is connecting the first prototype cell to the RestaurantViewController. This prototype cell looks like it's the "Basic" style of cell with a disclosure indicator accessory on the right. But look at the screenshot of your app running. The table is being populated with cells that appear to be the "Subtitle" style of cell without a disclosure indicator accessory on the right.
The reason that your segue is never firing no matter what you do is that the segue is only configured to work for a specific prototype cell, but that prototype cell is never being used when you populate the table. Whatever you're doing in tableView:cellForRowAtIndexPath:, you're not using the prototype cell that you want.
#Starscream has the right idea dequeueing the right cell with the right identifier and matching it with the identifier of the prototype cell in Interface Builder. The crash that you're getting even after doing that might be because of the previous problem mentioned in the comments above. Your segue in the storyboard is clearly pointing to a UITableViewController. Your code in prepareForSegue:sender: should be let vc = segue.destinationViewController as RestaurantViewController, as long as RestaurantViewController is a subclass of UITableViewController. You'll crash if you try to cast it as a UINavigationController. Also make sure that the class for the destination UITableViewController in the storyboard is listed as RestaurantController in the Identity Inspector pane. You'll crash if your program compiles thinking that the storyboard just contains a generic UITableViewController there.
Getting back to the original problem more, I don't know how you've implemented tableView:cellForRowAtIndexPath:, which might be crucial. Maybe it's not so simple. Maybe you plan on handling many prototype cells or generate custom cells at runtime. In this case, one way to make this simple for you is to programmatically perform the segue when the user taps on a cell. Instead of using a specific prototype cell, make the segue a connection originating from the "Restauranger nära mig" UITableViewController going to the RestaurantViewController. (Connect in Interface Builder by control-click dragging from the Table View Controller icon at the top of the first one over to the body of the second). You must give this segue an identifier in the Attributes Inspector pane to make this useful. Let's say it's "toRestaurant". Then at the end of your tableView:didSelectRowAtIndexPath: method, put this line of code: self.performSegueWithIdentifier("toRestaurant", sender: self). Now no matter what cell is selected in the table, this segue will always fire for you.
Try creating cells like this in your cellForRow method:
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("MyTestCell", forIndexPath: indexPath)
Im going out on a whim here since I am just getting into swift right now but the way I do it in my prepareForSegue() is something like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
//notice I changed [i] to [index!.row]
vc.data = currentResponse[index!.row] as NSArray
}
}
What it looks like to me is that you are calling the i variable which is kind of like a private variable inside a method of your class. You can do something like #Syed Tariq did with the selectRow variable and set it above your class SomeController: UIViewController /*, maybe some more here? */ { and then sign the variable inside your
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
method like above but both ways should work rather well.
I had the same problem and I found the solution to be:
performSegueWithIdentifier("toViewDetails", sender: self)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var cellnumber = procMgr.processos[indexPath.row].numero
println("You selected cell #\(indexPath.row)")
println(cellnumber)
performSegueWithIdentifier("toViewDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toViewDetails" {
let DestViewController : ViewDetails = segue.destinationViewController as! ViewDetails
}
}
You may need to get the selected cell index of the UItableview. Below code used the selected cell index (UItableview.indexPathForSelectedRow) to get a correct element of the array.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "seguaVisitCardDetial" {
let viewController = segue.destinationViewController as! VCVisitCardDetial
viewController.dataThisCard = self.listOfVisitCards[(tblCardList.indexPathForSelectedRow?.row)!]
}
}
I had this problem, too; the segue from UITableViewCell did not call.
After some searching, I found it is because I had chosen "No Selection" for "Selection" field.
I was following this tutorial http://www.raywenderlich.com/76519/add-table-view-search-swift when I ran into an error. I am adding this feature into an app I was already working on. Once I am in the booths table view, I want to be able to navigate out into the main menu with a button on the navigation bar. Here is the section of code that deals with the segues.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("BoothDetail", sender: tableView)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "BoothDetail" {
let BoothDetailViewController = segue.destinationViewController as UIViewController
if sender as UITableView == self.searchDisplayController!.searchResultsTableView {
let indexPath = self.searchDisplayController!.searchResultsTableView.indexPathForSelectedRow()!
let destinationTitle = self.filteredBooths[indexPath.row].name
BoothDetailViewController.title = destinationTitle
} else {
let indexPath = self.tableView.indexPathForSelectedRow()!
let destinationTitle = self.booths[indexPath.row].name
BoothDetailViewController.title = destinationTitle
}
}
}
}
The error is thrown while trying to use the back button on the booths list that is a direct show segue to the main conference menu. The error is on this line.
if sender as UITableView == self.searchDisplayController!.searchResultsTableView {
You have quite a few problems. Some fatal, some just a headache.
the first headache is you are calling the same segue twice. Both functions call the same segue. Both will execute. Now if you want a double animation, okay. But since one passes data and the other does not, you may have an issue. Eliminate the didSelectRowAtIndexPath function.
In your prepareForSegue method it appears you have two different objects connected to the same segue. A searchDisplayController and a tableView. You want two separate segues. Then your if/else makes changes based on which segue was chosen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue1" {
//code set 1
} else if segue.identifier == "segue2" {
//code set 2
}
}
I had similar problem and I just got it solved. When creating your segue in the tableview do not drag it from the cell, create a manual segue called "BoothDetail"
and connect it to BoothDetailViewController, to create a manual segue select the table view controller and click on "show connection inspector" you will see manual under triggered segue .
My problem the sender was a tableviewcell and not the tableview, so the function
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("BoothDetail", sender: tableView)
}
was never called to pass the tableview controller so when you try to cast it you were getting the error.
good luck