0

I want to preselect cells when table loads. I've followed this answer but it doesn't work properly. It doesn't always preselects the correct cells. https://stackoverflow.com/a/19296095/12114641 It only selects last cell. How to make it work properly?

Tableview is set to Single Selection.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as? TableViewCell else {
        fatalError("Can't find cell")
    }

    cell.title = dataSource[indexPath.row]        

    if globalCheckedArray[indexPath.row].checked {
        self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
    } else {
        self.tableView.deselectRow(at: indexPath, animated: false)
    }

    return cell
}

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

    globalCheckedArray[indexPath.row].checked = true

    if tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false {
        tableView.deselectRow(at: indexPath, animated: true)
        return nil
    }

    return indexPath
}

func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? {
    return nil
}
Raymond
  • 1,108
  • 13
  • 32
  • "It only selects last cell" Did you set your tableView with multiselection or only one? – Larme May 04 '20 at 16:40
  • Try moving this code to `willDisplayCell`. – koen May 04 '20 at 16:40
  • @Lame I've single selection. multiselection works but it breaks further select/deselect. If I enable multiselection tapping on cell doesn't select/deselect anymore. – Raymond May 04 '20 at 16:41
  • @koen moving it to 'willDisplayCell' doesn't work either. – Raymond May 04 '20 at 16:45
  • @Sh_Khan I'm showing Checkmark Accessory in cell. When I tap cell it shows/hides checkmark. – Raymond May 04 '20 at 16:49
  • @Raymond: what doesn't work? The link you provided has just that method in the accepted answer. You may need to rethink your `globalCheckedArray` logic. – koen May 04 '20 at 17:02
  • @koen It doesn't work as it doesn't preselect the cells which it is supposed to select. I'm printing globalCheckedArray values and they are correct. There seems to be something wrong with cell selection. – Raymond May 04 '20 at 17:16
  • @Raymond so what happens inside `willSelectRowAt` ? Looks like you are deselecting the row you want to select? – koen May 04 '20 at 17:21
  • @Raymond - when you're talking about "selected cells" do you mean showing the cell as **Highlighted**? Or showing the accessory **checkmark**? Or both? – DonMag May 04 '20 at 18:36
  • @DonMag Showing cell checkmarked. When cell is tapped I show checkmark. – Raymond May 05 '20 at 15:16
  • @koen it's select/deselect inside willSelectRow. Tap and it inverts selection. – Raymond May 05 '20 at 15:17
  • @Raymond - ok... so you want both highlighted **AND** checkmark? With multi-select or single-select only? – DonMag May 05 '20 at 15:23
  • @Raymond - oh wait... you mean you want a row (or multiple rows) to show **Highlighted** when the table appears? And when the user taps a **Highlighted** row it should get a Checkmark (or toggle the checkmark on/off), but remain **Highlighted**? – DonMag May 05 '20 at 15:26
  • @DonMag Sorry it's only checkmarked and not highlighted. There is no highlighting. As I've mentioned in question I want to get list of selected items from users. So user can tap to select more than one items (cell). When user selects items I mark it with Checkmark. Let's say user has selected 3 items which means three selected cells (options) are checkmarked. When user comes back to this view I want to show these selected items (checkmarked cells). At present when view loads it doesn't show checkmarks correctly. – Raymond May 05 '20 at 18:14

1 Answers1

0

You need to do a couple things...

  • use a data structure - likely an array of custom objects - to track the "selected" state
  • set the .accessoryType for the cell in cellForRowAt
  • pass an array of selected rows (selected objects) to the next view controller

Here is a very simple example. In your Storyboard, add a UITableViewController, set its custom class to TestTableViewController and embed it in a UINavigationController.

When you run this, you can select - that is, add checkmarks - to multiple rows.

When you tap the "Test" button in the navigation bar it will push to SampleViewController which will display the selected row titles in a label.

When you go "Back" your data will be retained, and the correct rows will have checkmarks.

struct MyDataStruct {
    var title: String = ""
    var isSelected: Bool = false
}

class TestTableViewController: UITableViewController {

    var myData: [MyDataStruct] = [
        MyDataStruct(title: "One", isSelected: false),
        MyDataStruct(title: "Two", isSelected: false),
        MyDataStruct(title: "Three", isSelected: false),
        MyDataStruct(title: "Four", isSelected: false),
        MyDataStruct(title: "Five", isSelected: false),
        MyDataStruct(title: "Six", isSelected: false),
        MyDataStruct(title: "Seven", isSelected: false),
        MyDataStruct(title: "Eight", isSelected: false),
        MyDataStruct(title: "Nine", isSelected: false),
        MyDataStruct(title: "Ten", isSelected: false),
        MyDataStruct(title: "Eleven", isSelected: false),
        MyDataStruct(title: "Twelve", isSelected: false),
        MyDataStruct(title: "Thirteen", isSelected: false),
        MyDataStruct(title: "Fourteen", isSelected: false),
        MyDataStruct(title: "Fifteen", isSelected: false),
        MyDataStruct(title: "Sixteen", isSelected: false),
        MyDataStruct(title: "Seventeen", isSelected: false),
        MyDataStruct(title: "Eighteen", isSelected: false),
        MyDataStruct(title: "Nineteen", isSelected: false),
        MyDataStruct(title: "Twenty", isSelected: false),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")

        let b = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(barButtonTapped(_:)))
        self.navigationItem.rightBarButtonItem = b
    }

    @objc func barButtonTapped(_ sender: Any?) -> Void {

        // instantiate the next view controller
        let vc = SampleViewController()

        // filter myData to only the elements with isSelected = true
        let selectedItems = myData.filter{ $0.isSelected == true }

        // set the next VC's property (i.e. pass the data to it)
        vc.theSelectedRows = selectedItems

        // push to the next VC
        self.navigationController?.pushViewController(vc, animated: true)

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
        let dataElement: MyDataStruct = myData[indexPath.row]
        cell.textLabel?.text = dataElement.title
        cell.accessoryType = dataElement.isSelected ? .checkmark : .none
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // toggle selected state in data
        myData[indexPath.row].isSelected = !myData[indexPath.row].isSelected
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }

}

class SampleViewController: UIViewController {

    var theSelectedRows: [MyDataStruct] = [MyDataStruct]()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        let resultLabel: UILabel = UILabel()
        view.addSubview(resultLabel)
        resultLabel.translatesAutoresizingMaskIntoConstraints = false
        resultLabel.numberOfLines = 0
        var s = ""
        theSelectedRows.forEach { d in
            s += d.title + "\n"
        }
        resultLabel.text = s
        resultLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        resultLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

}

Output:

enter image description here

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86