Get data from a cell with "willSelectRowAtIndexPath" in Swift2 - swift

I'm trying since two weeks with the help of many Tutorials to get and segue data from a TableView cell. But it seems it is impossible to do that in XCode. It is only possible to get the selectedRow of a cell, but I cannot read out the Text labels of a selected cell.
If a user selects a Cell I want to segue the value of a label in the selected cell to a new View Controller.
I can only submit the selected row to a new View Controller but not the value of a label in this selected cell.
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
row = indexPath.row // here is the row the user has selcted
labelStringOfselectedCell = "??????" // how to retrieve data from a label in the cell?
return indexPath
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController : ViewControllerDetail = segue.destinationViewController as! ViewControllerDetail
DestViewController.seguedData = labelStringOfselectedCell
}

If you really wanted to do something like that, you can do something like:
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
let cell = tableView.cellForRowAtIndexPath(indexPath) // do not confuse this with the similarly named `tableView:cellForRowAtIndexPath:` method that you've implemented
let string = cell?.textLabel?.text
return indexPath
}
Clearly it depends upon whether you're custom cell subclass and what the label was, but that illustrates the basic idea.
Having said that, you should not do this. Your app should be following Model-View-Controller (MVC) programming paradigm. When determining what data to pass to the next scene, you should go back to the original model you used when originally populating the table, not referring to some control in the table.
For example, let's imagine that you populated the original cell by retrieving data from the objects model:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let object = objects[indexPath.row]
cell.textLabel?.text = object.name
return cell
}
Then you would just implement a prepareForSegue that retrieves the data from objects also (and you don't have to implement willSelectRowAtIndexPath at all):
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? ViewControllerDetail {
let object = objects[tableView.indexPathForSelectedRow!.row]
destination.seguedData = object.name
}
}
Clearly, this will change depending upon what your model was and how you originally populated the cell, but hopefully it illustrates the basic idea.

Related

Saving Data in UITableview

I have an app whereby a user can add books to a favourites list, by populating a tableview. Details on the book are displayed in a view and by tapping a 'favourites', a segue is performed inputting info on that book into a tableview cell.
At present only one book can appear in the table at a time adding a new book will remove the initial entry (so in effect only the first cell of the tableview is ever used)
Is there a way to save each entry in the tableview so in effect a list of favourites is created
saveButton
#IBAction func saveButton(_ sender: Any) {
let bookFormat = formatLabel.text
if (bookFormat!.isEmpty)
{
displayMyAlertMessage(userMessage: "Please Select a Book Format")
return
}
else{
self.performSegue(withIdentifier: "LibViewSegue", sender: self)
}
}
TableView
extension LibrarybookViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 115
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(#function, dataSource.count)
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(#function, "indexPath", indexPath)
guard let bookCell = tableView.dequeueReusableCell(withIdentifier: "libCell", for: indexPath) as? LibrarybookTableViewCell else {
return UITableViewCell()
}
let libbook = dataSource[indexPath.row]
bookCell.cellTitleLabel.text = libbook.title
bookCell.cellReleaseLabel.text = libbook.release
bookCell.cellFormatLabel.text = bookFormat
return bookCell
}
I have been reading about defaults and CoreData but I'm not sure whether this should be implemented within the segue button action or within the tableview functions?
I see you have a dataSource array which contains list of books. At the simplest you could just append data to your dataSource, then reload your UITableView. But if you want persistent storage, you could look into local db solutions like SQLite, CoreData, or Realm. Then it would just be the matter of storing -> get data -> display on UITableView.
The general idea would be on add button tap (or whatever event listener you want to attach the action to), save it to persistent storage, reload your tableView, since the dataSource is from persistent storage, it'll update automatically. Suppose you load the data from persistent storage.
Also, don't store your array in UserDefaults, it's for bite sized data for things like user session etc.
Edit: as #Leo Dabus point out, indeed you can insert row without reloading the tableView by using
let index = IndexPath(row: items.count - 1, section: 0) // replace row with position you want to insert to.
tableView.beginUpdates()
tableView.insertRows(at: [index], with: .automatic)
tableView.endUpdates()

How to know which cell was tapped in tableView using Swift

I am using a tableView to display a list of names. When the user taps on one of the cells, a popup should come up showing more detail. I am using this function to check what cell the user has tapped.
var selectedCell: Int?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("This cell from the chat list was selected: \(indexPath.row)")
selectedCell = indexPath.row
}
Unfortunately, it seems to give give back random numbers between 0 and 2 (I currently have three cells that are displayed).
Do you know how I could know which cell was tapped?
Thank you in advance.
EDIT:
I understood what is falsing my results.
I am using this function to push which cell is selected to another view controller (named ConversationViewcontroller). But, the segue fonction is called before the tableView function, and so the segue pushes the previously selected cell. How could I correct this?
Here is the code for my segue:
//SEGUE: Cell was selected: pass chatID
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "OpenChat" {
if let toViewController = segue.destination as? ConversationViewController {
toViewController.chatIdentifier = selectedCell
}
}
}
Thanks again.
try this
var selectedCell: IndexPath
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("This cell from the chat list was selected: \(indexPath.row)")
selectedCell = indexPath
}
if you want use IndexPath
self.selectedCell?.row

pass data from tableview to view controller

I am writing a Xcode program in Swift. I have a tableview controller with some labels and an image per cell. They have their data from a first view controller. So far so good. Now i would like the user to tap a cell which opens a new controller which contains the same label and image data as the cell. With the following code the new controller opens, nonetheless I don't no how to transfer the data. If someone could help me I would be so so grateful.
Here are the codes that i tend to use:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "TableView"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableView
let picture = pic[indexPath.row]
cell.label1.text = picture.name1
cell.photoImage.image = picture.photo
cell.label2.text = picture.name2
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let cell = indexPath.row
self.performSegueWithIdentifier("segue", sender: cell)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue"{
var row = tableView.indexPathForSelectedRow
let viewController = segue.destinationViewController as! vc2
}
}
PS:I have a segue from the image to the new controller with the identifier "segue".
PPS: Of course i have tried the following method: Send data from TableView to DetailView Swift but when i run my program I get an error with the information that my labels were unexpectedly found nil
You should not need to override didSelectRowAtIndexPath or call performSegueWithIdentifier to do this. Connect your segue in the IB file dragging from a table view controller's cell to the second controller. You should then pass the data to the controller in prepareForSegue, in the segue.destinationViewController. You set the public properties on that destination controller.
Also make sure your labels in your prototype cell have been connected in IB. If they are they, should not be nil. Set a breakpoint on the cellForRowAtIndexPath to verify this.
Unless you are talking about the labels in your destination controller. These also need to be hooked up in IB. You would then set them in prepareForSegue. You would get the appropriate data from the
let viewController = segue.destinationViewController as! vc2
var path = tableView.indexPathForSelectedRow
viewController.destLabel.text = arrayData[path.row].myData // or whatever data you have from your model.

Passing the "correct" Data from the selected cell in a UITableViewController to a ViewController

I have a UITableviewController where I take a JSON file and break it down into various objects for display on my tableview (this works fine).
I've also created variables to store certain information from the cells generated in the tableview to be passed on to a detailViewController (text and images).
Using the 'prepare for segue' method I am able to pass the information I need to the detailViewController. However, I keep running into an issue where the data displayed in the detailViewController isn't the data of the cell selected but rather the data of what appears to the the last loaded cell on the table.
I am not using the didSelectRowAtIndexPath because it doesn't seem to do much better but create an additional segue screen (automatically).
In summary, my TableViewController displays about 4 cells at a time on the screen. regardless of which cell I select, the information passed to the DetailViewController is always the information on the fourth cell displayed on the screen.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Post Cell", forIndexPath: indexPath) as! TableViewCell
var dict = jsonArr[indexPath.row]
cell.postTitle.text = dict["data"]!["post_title"] as? String
cell.postTag.text = dict["data"]!["post_tags"] as? String
cell.postAddress.text = dict["data"]!["post_address"] as? String
titleToPass = cell.postTitle.text
postTagToPass = cell.postTag.text
addressToPass = cell.postAddress.text
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ListToDetail") {
let viewController = segue.destinationViewController as! DetailPostViewController
viewController.passedTitle = titleToPass
viewController.passedPostTags = postTagToPass
viewController.passedAddress = addressToPass
}
}
cellForRowAtIndexPath gets called for every cell, so it makes sense that your variables will always have values matching the last cell to be updated.
Instead of setting the data you want to pass in that method, call indexPathForSelectedRow: in prepareForSegue and use the row of the index path to get the right things from jsonArr.

Custom UITableViewCells repeating when navigating to viewcontroller

I am making a running app, and would like the user to see the races/marathons in their city. When clicking on the cell the user is navigated to a new view controller with details of the marathon. But upon pressing the back button to go back to the table the cells repeat. for example, if scrolling on the table it will display cell 1, cell 2,cell 3, cell 4,cell 1, cell 2,cell 3, cell 4 and will repeat more every time I navigate to the view controller with the table.
The code for the table is:
//MARK: TableView
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exhibitions.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: marathonTableViewCell = tableView.dequeueReusableCellWithIdentifier("marathonCell") as! marathonTableViewCell
singleCell.marathonName.text = exhibitions[indexPath.row]
singleCell.entryNumber.text = "\(entryNumber[indexPath.row])"
singleCell.entries.text = "\(entires[indexPath.row])"
return singleCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("marathonDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "marathonCardDetail"){
var upcoming: marathonDetailViewController = segue.destinationViewController as! marathonDetailViewController
let indexPath = self.marathonsTableView.indexPathForSelectedRow!
let currentCell = marathonsTableView.cellForRowAtIndexPath(indexPath) as! marathonTableViewCell
let marathonEvents = currentCell.marathonName.text
upcoming.nameMarathon = marathonEvents
self.marathonsTableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
I am using swift, Xcode 7, and Parse as my backend.
I think u have to init ur array before update tableview.
When u click back button, tableview may refresh.
So make some code that makes ur array new, and then call tableview.reloadData()