swiftsearchtableviewcheckmark

How to put a checkmark after search


Im trying to put checkmarks after doing a search. I've seen some answers here but it doesn't work for me. Ive tried some of the answers but it will only make an error.

Here's the code that I wrote.

struct finalCheckednames { var name: String; var checkMark: Bool}

class ShowBroadcastNamesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {

var nameslist = ["Mark","Chris", "James", "Sandra"]
var myIndex = 0
var nameArray = String()
var filteredData = [String]()
var isSearching = false
var selectedNamesToBroadcast = [String]()
var selectedNamesIndex:Int? = nil
var checkmarks = [Int : Bool]()
var hasChecked = Bool()

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

        let text: String!

        if isSearching {
            text = filteredData[indexPath.row]

        } else {
            text = nameslist[indexPath.row]
        }

        cell.configureCell(text: text)

        if checkmarks[indexPath.row] != nil {
            cell.accessoryType = checkmarks[indexPath.row]! ? .checkmark : .none
        } else {
            checkmarks[indexPath.row] = false
            cell.accessoryType = .none
        }

        return cell

    } else {

        return UITableViewCell()
    }
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  if isSearching {
            selectedNamesIndex = filteredData[indexPath.row] // im also getting an error here

        } else {

    selectedNamesIndex = indexPath.row
    let cell = tableView.cellForRow(at: indexPath)

    if let index = selectedNamesIndex {
        if (cell?.accessoryType == .checkmark) {
            cell!.accessoryType = .none
            checkmarks[indexPath.row] = false
            hasChecked = false

            let indexPathTapped = tableView.indexPath(for: cell!)
            let name = nameslist[(indexPathTapped!.row)]
            print(name, hasChecked)
            if let index = selectedNamesToBroadcast.index(of: name) {
                selectedNamesToBroadcast.remove(at: index)
            }

        } else {
            cell!.accessoryType = .checkmark
            checkmarks[indexPath.row] = true
            hasChecked = true

            let indexPathTapped = tableView.indexPath(for: cell!)
            let name = nameslist[(indexPathTapped!.row)]
            print(name, hasChecked)
            selectedNamesToBroadcast.append(name)

            let selectedNamesToBroadcast = name

            finalCheckednames(name: name, checkMark: hasChecked)
            print(finalCheckednames.self, "XXXX")
        }
    }

}

 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchBar.text == nil || searchBar.text == "" {
        isSearching = false
        view.endEditing(true)
        tableView.reloadData()
    } else {
        isSearching = true
        filteredData = namelist.filter{$0.range(of: searchText, options: .caseInsensitive) != nil}
        tableView.reloadData()

I was hoping for the checkmarks to stay after searching. So atleast i can save all the checkmarked items somewhere.


Solution

  • Your code is very, very, very complicated. The most efficient way to accomplish your request is to use the isChecked information in the struct, nothing else, no extra arrays, dictionaries, properties.

    Give the struct and the Bool a more meaningful name.

    struct Person {
        let name: String
        var isChecked: Bool
    }
    

    In the controller name the data source array people and the filter array filteredPeople. Further you need only isSearching, delete all other properties

    class ShowBroadcastNamesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
    
        var people = [Person(name: "Mark", isChecked: false),
                      Person(name: "Chris", isChecked: false),
                      Person(name: "James", isChecked: false),
                      Person(name: "Sandra", isChecked: false)]
    
        var filteredPeople = [Person]()
        var isSearching = false
    

    var myIndex = 0
    var nameArray = String()
    var filteredData = [String]()
    var selectedNamesToBroadcast = [String]()
    var selectedNamesIndex:Int? = nil
    var checkmarks = [Int : Bool]()
    var hasChecked = Bool()
    

    In cellForRow force unwrap the custom cell and name it uppercased (good practice). Pass the name to configureCell and set the accessory view depending on the value of isChecked

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BroadcastNamesTableViewCell
    
        let person = isSearching ? filteredPeople[indexPath.row] : people[indexPath.row]
        cell.configureCell(text: person.name)
        cell.accessoryType = person.isChecked ? .checkmark : .none
        return cell
    }
    

    In didSelectRow toggle isChecked (and update also the people array if necessary) and reload the row, that's all

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if isSearching {
            filteredPeople[indexPath.row].isChecked.toggle()
            let index = people.firstIndex{$0.name == filteredPeople[indexPath.row].name}!
            people[index].isChecked.toggle() 
        } else {
            people[indexPath.row].isChecked.toggle()
        }
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    

    In textDidChange search for the name

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        if searchText.isEmpty {
            isSearching = false
            filteredPeople.removeAll()
            view.endEditing(true)
        } else {
            isSearching = true
            filteredPeople = people.filter{$0.name.range(of: searchText, options: .caseInsensitive) != nil}
        }
        tableView.reloadData()
     }
    

    In this answer I already suggested a more efficient version of textDidChange, please consider to follow the advices.

    To get the checked people just write

    let checkedPeople = people.filter{$0.isChecked}