3

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
        }
    }

}
raheem
  • 689
  • 2
  • 8
  • 16
  • 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? – trungduc May 21 '18 at 04:55
  • 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 May 21 '18 at 04:59
  • have u got it? @trungduc – raheem May 21 '18 at 05:02
  • Yes, you can try to add this line `self.filterTableView.allowsMultipleSelection = true`. And check this answer for right way to handle check marks https://stackoverflow.com/questions/50112804/add-remove-multiple-checkmarks-on-selected-rows-in-swift-4-uitableview/50113066#50113066 – trungduc May 21 '18 at 05:02
  • 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 May 21 '18 at 05:05

4 Answers4

7

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.

HAK
  • 2,023
  • 19
  • 26
  • Bro your code preselect the cell perfectly but when i tried to select any other cell it does not deselect the previous one. @HAK – raheem May 21 '18 at 05:35
  • Its working fine for me. Make sure you are not doing anything in your didDeselect method. Just comment it out to check. – HAK May 21 '18 at 05:38
  • Also make sure tableView's selection property is set to single selction. – HAK May 21 '18 at 05:41
  • i have written in viewDidLoad , tableview.allowSelection = true. but its selecting more than one cell. @HAK – raheem May 21 '18 at 05:50
  • 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 May 21 '18 at 05:54
  • @raheem add these two lines in your viewDidLoad method: tableView.allowsSelection = true tableView.allowsMultipleSelection = false – HAK May 21 '18 at 05:55
  • so how can it make single selection . @HAK – raheem May 21 '18 at 05:55
  • 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. – HAK May 21 '18 at 05:58
  • bro still its selecting multiple cells :( . @HAK – raheem May 21 '18 at 06:02
  • You want one cell to be selected from a single section? And preselection is removed once second cell is selected from the section? – HAK May 21 '18 at 06:03
  • yes thats what i want and also i want the selected cell name also. @HAK – raheem May 21 '18 at 06:07
  • @raheem Edited! – HAK May 21 '18 at 06:14
  • yup got it, how can i get the name of labels that are selected? @HAK – raheem May 21 '18 at 06:26
  • Using the indexpaths selected. e.g: let label = array[indexpath.section][indexpath.row]. It mainly depends upon your datastructure that you are using. – HAK May 21 '18 at 06:28
  • i have to give section number static to get indivisual names? @HAK – raheem May 21 '18 at 06:42
  • 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] – HAK May 21 '18 at 06:47
  • how can i get the name through section wise? @HAK – raheem May 21 '18 at 07:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171441/discussion-between-hak-and-raheem). – HAK May 21 '18 at 07:04
  • @HAK why do we need another array to keep selected indexes while `UITableView` has `indexPathsForSelectedRows` property? https://developer.apple.com/documentation/uikit/uitableview/1614864-indexpathsforselectedrows?language=objc – trungduc Jun 04 '18 at 06:40
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
    }
iOS Geek
  • 4,825
  • 1
  • 9
  • 30
  • i have two sections in my table view section at 0 index and section at 1 index. @iOS Geek – raheem May 21 '18 at 05:01
  • I used three sections – iOS Geek May 21 '18 at 05:03
  • 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 – iOS Geek May 21 '18 at 05:04
  • there is no selected cell in your screenshots. @iOS Geek – raheem May 21 '18 at 05:07
  • 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 May 21 '18 at 05:08
  • please Check I just added a GIF that shows live working – iOS Geek May 21 '18 at 05:11
  • Bro i'm using simple checkmark in table view can show how it can be use in ur code? @iOS Geek – raheem May 21 '18 at 05:14
  • please check I updated cellForRow Code all other didSelect and DIdLoad code is to be used same as I did – iOS Geek May 21 '18 at 05:17
  • according to gif u r selecting single cell from whole table view but i want single cell from single section. @iOS Geek – raheem May 21 '18 at 05:17
  • 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 May 21 '18 at 05:18
  • my Section Array **["pizza", "deep dish pizza", "calzone"]** my sectionCode is in **ViewForHeaderInSection** – iOS Geek May 21 '18 at 05:20
1
  • 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;
    }
    
trungduc
  • 11,926
  • 4
  • 31
  • 55
0

indexPaths is a local array variable of type IndexPath, incase if you are confused of what it is.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    tableView.deselectRow(at: indexPath, animated: true)

    if indexPaths.count > 0 {
        tableView.cellForRow(at: indexPaths[0])?.accessoryType = .none

        if indexPaths[0] == indexPath {
            tableView.cellForRow(at: indexPath)?.accessoryType = .none
            self.indexPaths.removeAll()
            return
        }

        self.indexPaths.removeAll()
        tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
        self.indexPaths.append(indexPath)
    } else {
        self.indexPaths.append(indexPath)
        tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
    }
}