Search code examples
arraysswiftuitableviewbuttonmultipleselection

Issue in tableview cell selection and deselection using button in swift


code: in this code i have already selected cells ids in selectedRows once i reach the vc those cells are showing as selected and if i select and deselect the cells then getting correct values in array selectedRows and getting wrong names in selectedNamesArray why? initially when adding selected names in array then also mismatch in names indexes. where ami wrong?

class ViewController2: UIViewController {
@IBOutlet weak var tableView: UITableView!

var selectedRows = [32, 38, 89, 11]
var selectedNamesArray: [String] = []

var arrayModel: [SampleModel] = [SampleModel(id: 32, name: "Apple"),
                                 SampleModel(id: 38, name: "Smasung"),
                                 SampleModel(id: 22, name: "Black berry"),
                                 SampleModel(id: 76, name: "Nokia"),
                                 SampleModel(id: 12, name: "Real me"),
                                 SampleModel(id: 75, name: "Lenova"),
                                 SampleModel(id: 36, name: "Pocco"),
                                 SampleModel(id: 11, name: "Motorola"),
                                 SampleModel(id: 37, name: "Infinix"),
                                 SampleModel(id: 88, name: "China"),
                                 SampleModel(id: 89, name: "Jio")
]

override func viewDidLoad() {
    super.viewDidLoad()
    
    if !selectedRows.isEmpty{
        selectedNamesArray = arrayModel
            .filter { selectedRows.contains($0.id) }
            .map { $0.name }
    }
    print("existing names: \(selectedNamesArray) \n and ids: \(selectedRows)")
}
}

extension ViewController2: UITableViewDelegate, UITableViewDataSource{

func numberOfSections(in tableView: UITableView) -> Int {
    2
}

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableviewCell1", for: indexPath) as? TestTableviewCell1
    cell?.selectionStyle = .none
    cell?.titleLbl?.text = arrayModel[indexPath.row].name
    cell?.checkBtn.tag = indexPath.row
    
    cell?.isChecked = selectedRows.contains(arrayModel[indexPath.row].id)
    
    cell?.buttonAction = { [self] sender in
        
        if selectedRows.contains(arrayModel[indexPath.row].id) {
            if let indexToRemove = selectedRows.firstIndex(of: arrayModel[indexPath.row].id) {
                
                selectedRows.remove(at: indexToRemove)
                
                if indexToRemove < selectedNamesArray.count {
                    selectedNamesArray.remove(at: indexToRemove) // Remove corresponding name
                }
                //   selectedNamesArray.remove(at: indexToRemove) // Remove the corresponding name
                cell?.isChecked = false
            }
        } else {
            selectedRows.append(arrayModel[indexPath.row].id)
            selectedNamesArray.append(arrayModel[indexPath.row].name) // Add the corresponding name
            cell?.isChecked = true
        }
        print("Selected rows: \(selectedRows)")
        print("Selected names: \(selectedNamesArray)")
    }
    return cell!
}
}

class TestTableviewCell1: UITableViewCell{

@IBOutlet weak var titleLbl: UILabel!
@IBOutlet weak var checkBtn: UIButton!
var isChecked = false{
    didSet{
        checkBtn.backgroundColor = isChecked ? .blue : .green
    }
}
var buttonAction: ((_ sender: AnyObject) -> Void)?
@IBAction func buttonPressed(_ sender: UIButton) {
    self.buttonAction?(sender)
}
}

struct SampleModel{
var id: Int
var name: String
}

o/p:

existing names: ["Apple", "Smasung", "Motorola", "Jio"] and ids: [32, 38, 89, 11] //here also indexes are not matching with both array values

after doing some selection de selection

Selected rows: [32, 38, 89, 36] Selected names: ["Apple", "Smasung", "Motorola", "Pocco"] //agin mis match in indexes


Solution

  • When you filter an array, you get back an array in the same order as the original array.

    So, let's simplify things a little...

    var arrayModel: [SampleModel] = [
        SampleModel(id: 32, name: "Apple"),
        SampleModel(id: 76, name: "Nokia"),
        SampleModel(id: 11, name: "Motorola"),
        SampleModel(id: 89, name: "Jio"),
    ]
    
    var selectedRows = [11, 76]
    
    let a1 = arrayModel.filter { selectedRows.contains($0.id) }
            
    print("selectedRows:")
    print(selectedRows)
    
    print()
    
    print("Filtered:")
    print(a1)
            
    

    We get this output in the debug console:

    selectedRows:
    [11, 76]
    
    Filtered:
    [App.SampleModel(id: 76, name: "Nokia"), App.SampleModel(id: 11, name: "Motorola")]
    

    Seems like it's "out of order"?

    The .filter closure essentially says:

    create a new, empty returnArray
    

    then, loops through arrayModel with:

    is element.id in selectedRows?
    if yes, append element to returnArray
    

    With the above code, we get:

    32 / Apple    -- discard
    76 / Nokia    -- append
    11 / Motorola -- append
    89 / Jim      -- discard
    

    The order of the returned array doesn't care about the order of selectedRows ... it's just checking if .id is contained in selectedRows, so the order is:

    ["Nokia", "Motorola"]
    

    If you want to do something with the "names" in the order they were selected you'll need to sort selectedNamesArray to match the order of selectedRows.

    Or, instead of trying to maintain a selectedNamesArray throughout, create it when you want to do something with it, like this:

    var selectedNamesArray: [String] = []
            
    selectedRows.forEach { i in
        if let m = arrayModel.first(where: { $0.id == i }) {
            selectedNamesArray.append(m.name)
        }
    }
    
    print(selectedNamesArray)
    

    outputs:

    ["Motorola", "Nokia"]