Single cell Selection in table view in swift

16,665

Solution 1

You have to save selected indexes somewhere, may be in some array with different sections. Since you want to have first cells of each section pre-selected lets start with something like this:

   var selectedIndexes = [[IndexPath.init(row: 0, section: 0)], [IndexPath.init(row: 0, section: 1)]]

In above array we are saving two indexpaths. One is for first cell of first section and the second is for first cell of second section.

Now your cellForRow may check for existing indexpaths in the array like so:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! UITableViewCell
    cell.selectionStyle = .none
    cell.textLabel?.text = tableArray[indexPath.section][indexPath.row]

    let selectedSectionIndexes = self.selectedIndexes[indexPath.section]
    if selectedSectionIndexes.contains(indexPath) {
        cell.accessoryType = .checkmark
    }
    else {
        cell.accessoryType = .none
    }


    return cell
}

For single selection:

// For single selection
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)

    // If current cell is not present in selectedIndexes
    if !self.selectedIndexes[indexPath.section].contains(indexPath) {
        // mark it checked
        cell?.accessoryType = .checkmark

        // Remove any previous selected indexpath
        self.selectedIndexes[indexPath.section].removeAll()

        // add currently selected indexpath
        self.selectedIndexes[indexPath.section].append(indexPath)

        tableView.reloadData()
    }
}

Above code removes any previously selected cell and saves the new one. If the same cell is selected again and again it remains checked.

Solution 2

You can try following method to keep one cell selected at one time

**My arrays**

let section = ["pizza", "deep dish pizza", "calzone"]

let items = [["Margarita", "BBQ Chicken", "Peproni", "BBQ Chicken", "Peproni"], ["sausage", "meat lovers", "veggie lovers"], ["sausage", "chicken pesto", "prawns & mashrooms"]]

/// Lets keep index Reference for which cell is 
/// Getting selected
var selectedIndex : [Int:Int]?

/// Now in didLoad
override func viewDidLoad() {
   super.viewDidLoad()
   /// Initialise the Dictionary 
   selectedIndex = [Int:Int]()

   /// Set the Default value as in your case
   /// Section - 0 and IndexPath - 0
   /// i.e First cell
   selectedIndex?.updateValue(0, forKey: 0)
   mainTableView.delegate = self
   mainTableView.dataSource = self
   mainTableView.reloadData()
} 

func numberOfSections(in tableView: UITableView) -> Int {
   return section.count
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 40
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    /// my Header cell
    let headerCell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as! TableViewCell
    headerCell.titleLabel.text = self.section[section]
    headerCell.ButtonToShowHide.tag = section
    return headerCell.contentView

}

/// Now in CellForRowMethod

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.section][indexPath.row]

        /// Compare if current section is Available in reference Dict
        if let val = selectedIndex![indexPath.section]{
            /// If Yes
            /// Check IndexPath Row Value
            if indexPath.row == val{
                /// If row is found that is selected
                /// Make it highlight
                /// You can set A radio button
                cell.textLabel?.textColor = UIColor.red
            }
            else{
                /// Set default value for that section
                cell.textLabel?.textColor = UIColor.black
            }
        }
        /// If no
        else{
            /// Required to set Default value for all other section
            /// And
            /// Required to Update previous value if indexPath was selected
            /// In previouus index Section
            cell.textLabel?.textColor = UIColor.black
        }

        return cell
    }

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        /// Remove All values
        selectedIndex?.removeAll()
        /// Insert current Value
        selectedIndex?.updateValue(indexPath.row, forKey: indexPath.section)
        /// Reload TableView Fully
        /// Or you can keep Reference of Previous and New Value
        /// For not reloading All cells
        self.mainTableView.reloadData()
    }

Output

when TableView is Loaded

enter image description here

When row selected in same section

enter image description here

when row selected in other section

enter image description here

GIF Working - Updating Cell Label Color

enter image description here

Re-Upddate

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.section][indexPath.row]

        /// Compare if current section is Available in reference Dict
        if let val = selectedIndex![indexPath.section]{
            /// If Yes
            /// Check IndexPath Row Value
            if indexPath.row == val{
                /// If row is found that is selected
                /// Make it highlight
                /// You can set A radio button
                cell.accessoryType = .checkmark
            }
            else{
                /// Set default value for that section
                cell.accessoryType = .none
            }
        }
        /// If no
        else{
            /// Required to set Default value for all other section
            /// And
            /// Required to Update previous value if indexPath was selected
            /// In previouus index Section
            cell.accessoryType = .none
        }

        return cell
    }

Solution 3

  • Enable allowsMultipleSelection to select multiple cell at a time.
  • Use tableView(_:willSelectRowAt:) to handle selection. If there is a selected cell in |indexPath.section|, deselect this row

    func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
      let indexPathsForSelectedRows = tableView.indexPathsForSelectedRows ?? [IndexPath]()
    
      // If there is a selected cell in |indexPath.section|, do nothing
      for selectedIndexPath in indexPathsForSelectedRows {
        if selectedIndexPath.section == indexPath.section {
          tableView.deselectRow(at: selectedIndexPath, animated: true)
        }
      }
    
      return indexPath;
    }
    
Share:
16,665
raheem
Author by

raheem

Updated on December 05, 2022

Comments

  • raheem
    raheem over 1 year

    i have a table view in which i have two sections. Both sections contain 3 cells under it. Now when i select any cell it shows tick sign, but it selects one cell from both section. i have tried some code but it isn't working. I want that user can select a single cell from both sections and when the table view load its first cell should be preselected. How can i do that? i'm bit confused about preselection of cell.My code for cell selection is this,

     self.filterTableView.allowsSelection = true
    extension FiltersVC: UITableViewDelegate,UITableViewDataSource{
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sectionTitles[section]
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return menuItems[section].count
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = filterTableView.dequeueReusableCell(withIdentifier: "filterCell", for: indexPath) as! FiltersTableViewCell
    
        cell.tilteLbl.text = menuItems[indexPath.section][indexPath.row]
        cell.selectionStyle = UITableViewCellSelectionStyle.none
        cell.accessoryType = cell.isSelected ? .checkmark : .none
        cell.selectionStyle = .none
        cell.backgroundColor = UIColor.clear
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 30
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    
        let headerView = UIView()
        headerView.backgroundColor = #colorLiteral(red: 0.9130856497, green: 0.9221261017, blue: 0.9221261017, alpha: 1)
    
        let headerText = UILabel()
        headerText.textColor = UIColor.black
        headerText.adjustsFontSizeToFitWidth = true
        switch section{
        case 0:
            headerText.textAlignment = .center
            headerText.text = "LIST BY"
            headerText.backgroundColor = #colorLiteral(red: 0.9190355449, green: 0.9281349067, blue: 0.9281349067, alpha: 1)
            headerText.font = UIFont.boldSystemFont(ofSize: 20)
        case 1:
            headerText.textAlignment = .center
            headerText.text = "COUSINE"
            headerText.backgroundColor = #colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1)
            headerText.font = UIFont.boldSystemFont(ofSize: 20)
        default: break
    
        }
    
        return headerText
    
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.section == 0 {
            if let cell = filterTableView.cellForRow(at: indexPath) {
                cell.accessoryType = .checkmark
    
                let item = menuItems[indexPath.section][indexPath.row]
                UserDefaults.standard.set(item, forKey: "listBy")
                UserDefaults.standard.synchronize()
                filterBtn.isUserInteractionEnabled = true
                filterBtn.backgroundColor = #colorLiteral(red: 0.9529120326, green: 0.3879342079, blue: 0.09117665142, alpha: 1)
            }
        }
        else if indexPath.section == 1{
            if let cell = filterTableView.cellForRow(at: indexPath) {
                cell.accessoryType = .checkmark
    
                let item = menuItems[indexPath.section][indexPath.row]
                UserDefaults.standard.set(item, forKey: "Cuisine")
                UserDefaults.standard.synchronize()
                filterBtn.isUserInteractionEnabled = true
                filterBtn.backgroundColor = #colorLiteral(red: 0.9529120326, green: 0.3879342079, blue: 0.09117665142, alpha: 1)
            }
        }
    }
    
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        if indexPath.section == 0 {
            if let cell = filterTableView.cellForRow(at: indexPath as IndexPath) {
                cell.accessoryType = .none
            }
        }
        else if indexPath.section == 1{
            if let cell = filterTableView.cellForRow(at: indexPath as IndexPath) {
                cell.accessoryType = .none
            }
        }
    
    }
    
    • trungduc
      trungduc almost 6 years
      Can you explain your problem again? As I understand, when you select a cell, tick sign is displayed on 2 cell and now you want to select only one cell each at a time. Is it right?
    • raheem
      raheem almost 6 years
      Problem is that , i have a table view in that i have two sections and under each section there are three cells. When i select any cell from section 0 it deselect the cell selected from section 1 and when i select any cell from section 1 it deselect cell from section 0 . @trungduc
    • raheem
      raheem almost 6 years
      have u got it? @trungduc
    • trungduc
      trungduc almost 6 years
      Yes, you can try to add this line self.filterTableView.allowsMultipleSelection = true. And check this answer for right way to handle check marks stackoverflow.com/questions/50112804/…
    • raheem
      raheem almost 6 years
      i have tried this from this it allows multiple selections of cell from each section but i want single cell selection from each section. @trungduc
  • raheem
    raheem almost 6 years
    i have two sections in my table view section at 0 index and section at 1 index. @iOS Geek
  • iOS Geek
    iOS Geek almost 6 years
    I used three sections
  • iOS Geek
    iOS Geek almost 6 years
    I updated Screenshots that may make you clear about my sections and its cells inside, You just need to copy my cellForRow Comparison code and didSelect Code for updating dictionary used here for reference selected index, this will basically let you select only one cell at one time from any of your section, I just changed textColor there you need to modify your check box
  • raheem
    raheem almost 6 years
    there is no selected cell in your screenshots. @iOS Geek
  • iOS Geek
    iOS Geek almost 6 years
    I modified text color one in red is selected others are black means unselected , check my cellforRow code I just updated cell.textLabel?.textColor in place of this you need to set your checkBox
  • iOS Geek
    iOS Geek almost 6 years
    please Check I just added a GIF that shows live working
  • raheem
    raheem almost 6 years
    Bro i'm using simple checkmark in table view can show how it can be use in ur code? @iOS Geek
  • iOS Geek
    iOS Geek almost 6 years
    please check I updated cellForRow Code all other didSelect and DIdLoad code is to be used same as I did
  • raheem
    raheem almost 6 years
    according to gif u r selecting single cell from whole table view but i want single cell from single section. @iOS Geek
  • raheem
    raheem almost 6 years
    how it differentiate in sections? @trungduc
  • iOS Geek
    iOS Geek almost 6 years
    in my tableView I have used 3 sections check my sectionArray and in Number OfSections I did returned section array count and in didSelect I am maintaining a dictionary with reference of selectedSection and selected Cell index
  • iOS Geek
    iOS Geek almost 6 years
    my Section Array ["pizza", "deep dish pizza", "calzone"] my sectionCode is in ViewForHeaderInSection
  • raheem
    raheem almost 6 years
    Bro your code preselect the cell perfectly but when i tried to select any other cell it does not deselect the previous one. @HAK
  • HAK
    HAK almost 6 years
    Its working fine for me. Make sure you are not doing anything in your didDeselect method. Just comment it out to check.
  • HAK
    HAK almost 6 years
    Also make sure tableView's selection property is set to single selction.
  • raheem
    raheem almost 6 years
    i have written in viewDidLoad , tableview.allowSelection = true. but its selecting more than one cell. @HAK
  • raheem
    raheem almost 6 years
    let me try. @trungduc
  • HAK
    HAK almost 6 years
    Here, we are not using tableview's own selection functionality. We are storing the indexes by ourselves instead and adding and removing checkmarks manually after checking the indexes.
  • HAK
    HAK almost 6 years
    @raheem add these two lines in your viewDidLoad method: tableView.allowsSelection = true tableView.allowsMultipleSelection = false
  • raheem
    raheem almost 6 years
    so how can it make single selection . @HAK
  • HAK
    HAK almost 6 years
    When it draws a cell in cellForRow, it checks for the indexpath of the cell to be contained in selectedIndexes array. If it is there, it adds a checkmark. If not it removes it. Similarly when you select a cell, didSelect is called which checks if the current index is in the array, if it finds it, it removes it and removes the checkmark from the cell. Otherwise if index is not present it adds the index in the selectedIndexes array and adds the checkmark to the cell.
  • raheem
    raheem almost 6 years
    bro still its selecting multiple cells :( . @HAK
  • HAK
    HAK almost 6 years
    You want one cell to be selected from a single section? And preselection is removed once second cell is selected from the section?
  • raheem
    raheem almost 6 years
    getting this error. Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value in for loop. @trungduc
  • raheem
    raheem almost 6 years
    yes thats what i want and also i want the selected cell name also. @HAK
  • HAK
    HAK almost 6 years
    @raheem Edited!
  • raheem
    raheem almost 6 years
    let me try. @trungduc
  • raheem
    raheem almost 6 years
    yup got it, how can i get the name of labels that are selected? @HAK
  • HAK
    HAK almost 6 years
    Using the indexpaths selected. e.g: let label = array[indexpath.section][indexpath.row]. It mainly depends upon your datastructure that you are using.
  • raheem
    raheem almost 6 years
    i have to give section number static to get indivisual names? @HAK
  • HAK
    HAK almost 6 years
    Just as you are setting the text in your cellForRow method you can access the cell name in didSelectRow method like let name = menuItems[indexPath.section][indexPath.row]
  • raheem
    raheem almost 6 years
    how can i get the name through section wise? @HAK
  • HAK
    HAK almost 6 years
  • trungduc
    trungduc almost 6 years
    @HAK why do we need another array to keep selected indexes while UITableView has indexPathsForSelectedRows property? developer.apple.com/documentation/uikit/uitableview/…