please help me to understand why the tableView.indexPathForSelectedRow method returns nil.
I want to make a transfer from Table View Controller to View Controller. I have a segue by a StoryBoard and leadingSwipeActions.
import UIKit
class TableViewController: UITableViewController {
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return months.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = months[indexPath.row]
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editItem" {
let path = tableView.indexPathForSelectedRow
print("\(path)") ##// Prints nil.##
}
}
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let editAction = UIContextualAction(style: .normal, title: "Edit Item") { [self] (action, view, completion) in
performSegue(withIdentifier: "editItem", sender: self)
}
editAction.backgroundColor = .systemOrange
return UISwipeActionsConfiguration(actions: [editAction])
}
}
Leading swipe - and subsequent tap of the action button - does not select the row.
Since the sender
parameter of the "prepare for segue" method is defined as Any?
, one approach would be to pass the indexPath
when you call the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editItem" {
// make sure an Index Path was passed as the sender
if let path = sender as? IndexPath {
print("Index Path: \(path)")
}
}
}
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
// note: use [weak self] to avoid possible retain cycle
let editAction = UIContextualAction(style: .normal, title: "Edit Item") { [weak self] (action, view, completion) in
guard let self = self else { return }
// pass the path as the sender
self.performSegue(withIdentifier: "editItem", sender: indexPath)
}
editAction.backgroundColor = .systemOrange
return UISwipeActionsConfiguration(actions: [editAction])
}
Edit - in response to comment
The sender: Any?
parameter means that sender
can be any object. That allows you to differentiate what action or piece of code initiated the segue.
Examples:
self.performSegue(withIdentifier: "editItem", sender: indexPath)
self.performSegue(withIdentifier: "editItem", sender: 1)
self.performSegue(withIdentifier: "editItem", sender: 2)
self.performSegue(withIdentifier: "editItem", sender: "Hello")
self.performSegue(withIdentifier: "editItem", sender: self)
self.performSegue(withIdentifier: "editItem", sender: myButton)
Then, in prepare(...)
, you can evaluate the sender
to decide what to do next.
Quite often, when using table views to "push" to another controller that relates to the tapped cell (a "details" controller, for example), the developer will connect a segue from the cell prototype to Details view controller. Then (if you gave that segue an identifier of "fromCell"), you can do something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "fromCell" {
if let cell = sender as? UITableViewCell {
if let path = tableView.indexPath(for: cell) {
print("Index Path: \(path)")
// here we might get data for that row/section,
// and pass it to the next controller
}
}
}
}
In your case, we send the indexPath
of the cell we just acted on as the sender
parameter.