Reputation: 63
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
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
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
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
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