How do I get the value of a selected row in a table view?

I have a table view and an array of strings. When I swipe a row, I want to add the clicked string to an array of strings. How do I get the value stored in the table view row. I have it set up like this:


import UIKit

var myStringArray: [String]?

var someStringArray: [String] = ["Hot Dogs", "Soda", "Chips", "Hamburgers", "Plates", "Dessert", "Napkins", "Fruit",
"Potatoe Salad", "Brats"]


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return someStringArray.count
    }
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
    cell.textLabel?.text = someStringArray[indexPath.row]
    cell.textLabel?.adjustsFontSizeToFitWidth = true
    cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
    return cell
}

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        
    let object: String = someStringArray[indexPath.row]
        
    let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
            
    let alert = UIAlertController(title: "Add ", message: "Are you sure you want to add '\(object)' to your list?", preferredStyle: .alert)
            
    alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
        actionPerformed(false)
        }))

    alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
        self.addObject()
           
        }))
                
        self.present(alert, animated: true)
    }
    
    return UISwipeActionsConfiguration(actions: [add, taken])
}

func addObject(index: Int) {
            
    let index = table.indexPathForSelectedRow
            
    let _index = (index?[someStringArray.hashValue])!
            
    if myStringArray!.isEmpty {
        myStringArray?.insert(someStringArray[_index], at: 0)
    } else {
        myStringArray?.append(someStringArray[_index])
    }
}
        

Accepted Reply

Are you sure you made any selection in tableView ?


To avoid crash, test for nil, with :


guard let index = top30QuarterbacksTable.indexPathForSelectedRow else { return }     // Do not add player
// After this, continue with your code



But you should ease your life.


Change paramet in addPlayer to the full indexPath

            alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in 
                self.addPlayer(index: indexPath) 
            }))


Then, no need to look for selected.

It is still safer to test for nil:


   func addPlayer(index: IndexPath) { 
         
        guard let cell = top30QuarterbacksTable.cellForRow(at: index) else { return }          // Should never occur
             
        let string = objectsArray[0] 
        cell.textLabel?.text = string 
             
        if myRoster!.isEmpty { 
                myRoster?.insert(string, at: 0) 
         } else { 
                myRoster?.append(string) 
          } 
    }

Replies

To get the cell content:


tableView.cellForRow(at: indexPath)


So, in addObject (I assume table is the tableView declared as an IBOutlet)


func addObject(index: Int) {
          
    let index = table.indexPathForSelectedRow
    let cell = table.cellForRow(at: index)


Note: why do you need _index ?

I don't need _index. I was just trying different things. I implemented your method I am getting an error at runtime at line 3. Unexpectedly found nil while unwrapping an Optional value.

You do not show what table is and how defined.

I guessed it was the tableView. Correct ?


You say error (Unexpectedly found nil while unwrapping an Optional value) at line 3 ? Here ?

let index = table.indexPathForSelectedRow


But you call table.table.indexPathForSelectedRow at line 3 in your original code, so there should not be an error here if your code compiles.


func addObject(index: Int) { 
             
    let index = table.indexPathForSelectedRow 
             
    let _index = (index?[someStringArray.hashValue])! 
             
    if myStringArray!.isEmpty { 
        myStringArray?.insert(someStringArray[_index], at: 0) 
    } else { 
        myStringArray?.append(someStringArray[_index]) 
    } 
}


So, change next line into


if  let cell = table.cellForRow(at: index)  {
     // Do what you need with cell, such as
     let theString = cell.textLabel?.text
}

I can't figure it out. On line 34 of my original post, I don't have anything in addOject() parameters. What am I supposed to pass through its parameters? Thank you for your help.

This is the actual file from my app. Can you help me please? When I run it, line 71 is throwing an error that it found nil while unwrapping a nil value.


import UIKit

var myRoster: [String]?

struct DataToPass {
    var playerData: String = ""
}

@available(iOS 11.0, *)
class Top_30_Quarterbacks: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var objectsArray: [String] = ["1. Patrick Mahomes, KC", "2. Deshaun Watson, HOU", "3. Aaron Rodgers, GB", "4. Matt Ryan, ATL", "5. Baker Mayfield, CLE", "6. Carson Wentz, PHI", "7. Jared Goff, LAR", "8. Cam Newton, CAR", "9. Andrew Luck, IND", "10. Drew Brees, NO", "11. Ben Roethlisberger, PIT", "12. Dak Prescott, DAL", "13. Russell Wilson, SEA", "14. Tom Brady, NE", "15. Lamar Jackson, BAL", "16. Mitchell Trubisky, CHI",  "17. Jameis Winston, TB", "18. Philip Rivers, LAC", "19. Kirk Cousins, MIN", "20. Derek Carr, OAK", "21. Sam Darnold, NYJ", "22. Josh Allen, BUF", "23. Matthew Stafford, DET", "24. Marcus Mariota, TEN", "25. Jimmy Garoppolo, SF", "26. Andy Dalton, CIN", "27. Eli Manning, NYG", "28. Nick Foles, JAC", "29. Joe Flacco, DEN", "30. Ryan Fitzpatrick, MIA"
    ]
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return objectsArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
        cell.textLabel?.text = objectsArray[indexPath.row]
        cell.textLabel?.adjustsFontSizeToFitWidth = true
        cell.textLabel?.font = UIFont.systemFont(ofSize: 22)
        return cell
       }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        top30QuarterbacksTable.deselectRow(at: indexPath, animated: true)
    }
    
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        
        let player: String = objectsArray[indexPath.row]
        
        let add = UIContextualAction(style: .normal, title: "Add") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
            let alert = UIAlertController(title: "Add Player", message: "Are you sure you want to add '\(player)' to your roster?", preferredStyle: .alert)
            
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
                actionPerformed(false)
            }))
            
            alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
                self.addPlayer(index: indexPath.row)
            }))
            
            self.present(alert, animated: true)
        }
        add.backgroundColor = .systemGreen
        
        let taken = UIContextualAction(style: .normal, title: "Taken") { (contextualAction, view, actionPerformed: @escaping (Bool) -> Void) in
            
            let alert = UIAlertController(title: "Player Taken", message: "Are you sure you want to mark '\(player)' as not available?", preferredStyle: .alert)
            
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { (alertAction) in
                actionPerformed(false)
            }))
            
            alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in
                
                
            }))
            
            self.present(alert, animated: true)
        }
        taken.backgroundColor = .systemRed
        return UISwipeActionsConfiguration(actions: [taken, add])
    }
    
    func addPlayer(index: Int) {
        
        let index = top30QuarterbacksTable.indexPathForSelectedRow!
        
        if let cell = top30QuarterbacksTable.cellForRow(at: index) {
            
            let string = objectsArray[0]
            
            cell.textLabel?.text = string
            
            if myRoster!.isEmpty {
                myRoster?.insert(string, at: 0)
            } else {
                myRoster?.append(string)
            }
        }
    }
    
    func markAsTaken() {
        
        
        
    }
    
    
    @IBOutlet weak var top30QuarterbacksTable: UITableView!
    @IBOutlet var view1: UIView!
    @IBOutlet weak var view2: UIView!
    @IBOutlet weak var label: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
    
    }
}

Are you sure you made any selection in tableView ?


To avoid crash, test for nil, with :


guard let index = top30QuarterbacksTable.indexPathForSelectedRow else { return }     // Do not add player
// After this, continue with your code



But you should ease your life.


Change paramet in addPlayer to the full indexPath

            alert.addAction(UIAlertAction(title: "Yes", style: .destructive, handler: { (alertAction) in 
                self.addPlayer(index: indexPath) 
            }))


Then, no need to look for selected.

It is still safer to test for nil:


   func addPlayer(index: IndexPath) { 
         
        guard let cell = top30QuarterbacksTable.cellForRow(at: index) else { return }          // Should never occur
             
        let string = objectsArray[0] 
        cell.textLabel?.text = string 
             
        if myRoster!.isEmpty { 
                myRoster?.insert(string, at: 0) 
         } else { 
                myRoster?.append(string) 
          } 
    }

That helped the app not to crash but it is still finding nil. How do I tell the app that the swiped row is the selected row? Thank you

    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

does not make a selection, hence the nil value.


Did you read and test the second part of my post ?


Other point :

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
        top30QuarterbacksTable.deselectRow(at: indexPath, animated: true) 
    }


Line 2: Why do you deselect as soon as selected ?!

That makes the selection nil.

Thank you. Yes it worked.