Home > Software engineering >  How to save checkmark status of tableview cell xcode 12 swift 5
How to save checkmark status of tableview cell xcode 12 swift 5

Time:10-19

I have a simple app similar to to-do list. I used TableViewController and I was able to set the checkmark accessory type for each row, also found a way to save the rows data so that when the app relaunches the list of items are shown again.

I want to save the checkmark state for all the rows. I tried other queries in Stack Overflow most of them are outdated.

here's my code to the app


class ViewController: UITableViewController {
    
    var shoppingList = [ShoppingList]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Shopping List"
       
        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareTapped))
        
        let clearList = UIBarButtonItem(title: "Clear List", style: .plain, target: self, action: #selector(clearList))
        
        toolbarItems = [clearList]
        navigationController?.isToolbarHidden = false
        
        load()
        
        tableView.tableFooterView = UIView() // clears the unused seperator lines
   
    }
    
    @objc func clearList() {
        
        shoppingList.removeAll(keepingCapacity: true)
        tableView.reloadData()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        shoppingList.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
        cell.textLabel?.text = shoppingList[indexPath.row].itemName
        cell.accessoryType = shoppingList[indexPath.row].checkmarkState ? .checkmark : .none
        return cell
    }
    
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            shoppingList.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCell.AccessoryType.checkmark {
            tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.none
            shoppingList[indexPath.row].checkmarkState = false
        } else {
            tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.checkmark
            shoppingList[indexPath.row].checkmarkState = true
        }
        
        save()
    }

    
    @objc func addTapped() {
        let ac = UIAlertController(title: "New Shopping Item", message: nil, preferredStyle: .alert)
        ac.addTextField()
        
        let addAction = UIAlertAction(title: "Add", style: .default) {
            [weak self, weak ac] _ in
            guard let item = ac?.textFields?[0].text else { return }
            self?.addItem(item)
        }
        
        ac.addAction(addAction)
        
        ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        
        present(ac, animated: true)
    }
    
    @objc func shareTapped() {

    }
    
    func addItem(_ item: String) {
        let itemCapitalised = item.capitalized
        shoppingList.insert(ShoppingList(itemName: itemCapitalised), at: 0)
        
        let indexPath = IndexPath(row: 0, section: 0)
        tableView.insertRows(at: [indexPath], with: .automatic)
        save()
    }
    
    func showErrorMessage(errorTitle: String, errorMessage: String) {
        let ac = UIAlertController(title: errorTitle, message: errorMessage, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "Ok", style: .default))
        present(ac, animated: true)
    }
    
    func save() {
        let jsonEncoder = JSONEncoder()
        
        if let savedData = try? jsonEncoder.encode(shoppingList) {
            let defaults = UserDefaults.standard
            defaults.set(savedData, forKey: "shoppingList")
        } else {
            print("Failed to save people.")
        }
    }
    
    func load() {
        let defaults = UserDefaults.standard
        
        if let savedList = defaults.object(forKey: "shoppingList") as? Data {
            let jsonDecoder = JSONDecoder()
            
            do {
                shoppingList = try jsonDecoder.decode([ShoppingList].self, from: savedList)
            } catch {
                print("Failed to load List")
            }
        }
    }
}

CodePudding user response:

As you already have stored checkmarkState in your model, simply add cell.accessoryType = shoppingList[indexPath.row].checkmarkState ? .checkmark : .none in tableView(_:cellForRowAt:) to render the checkmark state.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
    cell.textLabel?.text = shoppingList[indexPath.row].itemName
    cell.accessoryType = shoppingList[indexPath.row].checkmarkState ? .checkmark : .none
    return cell
}

Also, there is a better way to write your tableView(_:didSelectRowAt:): Just invert the checkmarkState, then reload the row.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
    shoppingList[indexPath.row].checkmarkState = !shoppingList[indexPath.row].checkmarkState
    tableView.reloadRows(at: [indexPath], with: .automatic)
    save()
}

Try to keep in mind the MVC philosophy. Never determine the state of your model by the UI view. Only determine the state of UI view by the model. When you receive user input, change the model directly, then re-render the view accordingly. Keep the model as single source of truth.

  • Related