Shubh9718
Shubh9718

Reputation: 63

Save the state of checkmarked cell in table view in swift 4.2

I have made a table view in a view controller and populating it programmatically. I am able to checkmark on cell at a time as i wanted but when i come back to that screen, it is not there.

These are my arrays:

let devCourses = [("Sound"),("Vibrate"),("Both"),("None")]

let devCousesImages = [UIImage(named: "speaker"), UIImage(named: "Group 1094"), UIImage(named: "Group 1093"), UIImage(named: "speaker-1")]

Here is my code for didSelectRowAt:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark

    }

Here is the code for didDeselectRowAt:

func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        tableView.cellForRow(at: indexPath)?.accessoryType = .none
    }



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

        let courseTitle = devCourses[indexPath .row]

        let Images = devCousesImages[indexPath .row]

        cell.label.text=courseTitle
        cell.label.textColor = UIColor.darkGray
        cell.photo.image=Images

        return cell
    }

I want the checkmark to be there when i get back to the screen

Upvotes: 1

Views: 1526

Answers (4)

vadian
vadian

Reputation: 285059

First of all rather than multiple arrays use a struct for the data source.

And don't use extra arrays or dictionaries to maintain the selection either, this is error-prone and unnecessarily expensive.

Add a boolean member to the struct for the selected state.

struct Course : Codable {
    let name : String
    let image : String
    var isSelected : Bool
}

Declare the data source array

var courses = [Course(name: "Sound", image: "speaker", isSelected: false),
               Course(name: "Vibrate", image: "Group 1094", isSelected: false),
               Course(name: "Both", image: "Group 1093", isSelected: false),
               Course(name: "None", image: "speaker-1", isSelected: false)]

In cellForRowAt set the checkmark depending on isSelected

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! VibTableViewCell
    let course = courses[indexPath.row]
    cell.label.text = course.name
    // define the text color in Interface Builder
    // cell.label.textColor = UIColor.darkGray
    cell.photo.image = UIImage(named: course.image)
    cell.accessoryType = course.isSelected ? .checkmark : .none

    return cell
}

In didSelectRowAt toggle the isSelected value in the data source and reload the row

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    courses[indexPath.row].isSelected.toggle()
    tableView.reloadRows(at: indexPath, with: .none)
}

Finally delete the method didDeselectRowAt

To save the array you could write

do {
    let data = try JSONEncoder().encode(courses)
    UserDefaults.standard.set(data, forKey: "Courses")
} catch { print(error) }

Upvotes: 0

Gleb
Gleb

Reputation: 176

You can use my solution.

Save state of your cells in plist file with NSCoder. Even you restart your app - you don't lose your data.

import UIKit

class TableViewController: UITableViewController {

    var itemsArray : [Item] = [Item(name: "mom", done: true), Item(name: "pap", done: false), Item(name: "Lena", done: false), Item(name: "Gleb", done: false), Item(name: "Sasha", done: false), Item(name: "Max", done: false)]


    /* path to custom plist file */
    let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("Items.plist")

    override func viewDidLoad() {
        super.viewDidLoad()

        if let filePath = dataFilePath {
            print(filePath)
        }

        loadData()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return itemsArray.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let item = itemsArray[indexPath.row]
        cell.accessoryType = item.done ? .checkmark : .none
        cell.textLabel?.text = item.name
        // Configure the cell...

        return cell
    }

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

        itemsArray[indexPath.row].done = !itemsArray[indexPath.row].done

        /* when state is changed - save it to plist file */
        saveDate()

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


    /* save array to plist file (you can see it inside folder from consol) */
    func saveDate() {
        let encoder = PropertyListEncoder()
        do {
            let data = try encoder.encode(itemsArray)
            try data.write(to: dataFilePath!)
        } catch {
            print(error.localizedDescription)
        }
        self.tableView.reloadData()
    }


    /* load data from plist file */
    func loadData() {

        if let data = try? Data(contentsOf: dataFilePath!) {
            let decoder = PropertyListDecoder()
            do {
                itemsArray = try decoder.decode([Item].self, from: data)
            } catch {
                print(error.localizedDescription)
            }
        }

    }
}

dataModel:

import Foundation

struct Item : Encodable, Decodable {
    var name : String = ""
    var done : Bool = false
}

Upvotes: 1

Vikash Kumar
Vikash Kumar

Reputation: 636

As you want to come back to view controller to show selected cell. then you are to maintain selected cell array and after that in viewDidLoad/ viewWillappear to set selected cell as below code

for index in selectedArray {
            self.tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none)
        }

Upvotes: 0

abcross92
abcross92

Reputation: 33

Make a dictionary [IndexPath:Bool] to keep track of the state of which rows are selected.

var dict: [IndexPath:Bool] = [:]

in tableView(_:, didSelectRowAt:) add the following line:

dict[indexPath] = true

in tableView(_:, didDeselectRowAt:) add the following line:

dict[indexPath] = false

in tableView(_:, cellForRowAt:) add the following lines:

let selected = dict[indexPath] ?? false
cell.accessoryType = selected ? .checkmark : .none

Upvotes: 0

Related Questions