unable to save on/off state of a UITableViewCell? - swift

there are two attributes 'time' and 'isOn' (string, bool) in the entity named 'Item'
in viewcontroller class I am able to give default condition to 'isOn' attribute (in savePressed function) which makes switchbtn.isOn = true and saves it in the data model for that particular 'time'
viewcontroller class :-
class ViewController: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var timePickerView: UIDatePicker!
#IBOutlet weak var timeLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
timePickerView.setValue(UIColor.white, forKeyPath: "textColor")
dateFormat()
// Do any additional setup after loading the view.
}
#IBAction func savePressed(_ sender: UIBarButtonItem) {
let entity = Item(context: context)
entity.time = timeLbl.text
entity.isOn = true
saveData()
self.dismiss(animated: true, completion: nil)
}
#IBAction func cancelPressed(_ sender: UIBarButtonItem) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func valueChanged(sender:UIDatePicker, forEvent event: UIEvent){
dateFormat()
}
func saveData() {
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
func dateFormat() {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
formatter.timeStyle = .short
timeLbl.text = formatter.string(from: timePickerView.date)
}
}
viewcontroller
in this class I am able to fetch and show the core data but don't know how to save the state of the cell switch button and update the data model as there is no use of 'didSelectRowAt' function
tableview class :-
class TableViewController: UITableViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items = [Item]()
override func viewDidLoad() {
super.viewDidLoad()
print(arr)
}
override func viewWillAppear(_ animated: Bool) {
getData()
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! TableViewCell
cell.timeLbl.text = items[indexPath.row].time
cell.switchBtn.isOn = items[indexPath.row].isOn
return cell
}
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
items = try context.fetch(Item.fetchRequest())
}catch{
print("failed to get the data")
}
}
}
tableview
in this I am able to print the current state of the switch but cannot access the 'items[indexPath.row]' from the tableview class
cell class :-
class TableViewCell: UITableViewCell {
#IBOutlet weak var timeLbl: UILabel!
#IBOutlet weak var switchBtn: UISwitch!
var alarm = Bool()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func valChange(_ sender: UISwitch) {
if sender.isOn{
switchBtn.isOn = true
}else {
switchBtn.isOn = false
}
}
}

In Swift the most efficient way is a callback closure.
In the cell add a property callback with a closure passing a Bool value and no return value. Call the callback when the value of the switch changed.
class TableViewCell: UITableViewCell {
#IBOutlet weak var timeLbl: UILabel!
#IBOutlet weak var switchBtn: UISwitch!
var alarm = Bool()
var callback : ((Bool) -> Void)?
#IBAction func valChange(_ sender: UISwitch) {
callback?(sender.isOn)
}
}
In cellForRow in the controller add the callback, in the closure update the model.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! TableViewCell
let item = items[indexPath.row]
cell.timeLbl.text = item.time
cell.switchBtn.isOn = item.isOn
cell.callback = { newValue in
self.items[indexPath.row].isOn = newValue
}
return cell
}
If cells can be inserted, deleted or moved you have to pass also the cell to get the actual index path
class TableViewCell: UITableViewCell {
#IBOutlet weak var timeLbl: UILabel!
#IBOutlet weak var switchBtn: UISwitch!
var alarm = Bool()
var callback : ((UITableViewCell, Bool) -> Void)?
#IBAction func valChange(_ sender: UISwitch) {
callback?(self, sender.isOn)
}
}
and
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! TableViewCell
let item = items[indexPath.row]
cell.timeLbl.text = item.time
cell.switchBtn.isOn = item.isOn
cell.callback = { currentCell, newValue in
let currentIndexPath = tableView.indexPath(for: currentCell)!
self.items[currentIndexPath.row].isOn = newValue
}
return cell
}

Related

Label.text Error! - Unexpectedly found nil while implicitly unwrapping an Optional value

I am new to iOS development and I found an error that I can not get past. I have read a lot online and on Stackoverflow but I don't understand why this error keeps coming up.
Upon testing and doing breakpoints I think I was able to get the data needed problem is when I display it on screen wit uilabel.
import UIKit
class ArtistViewController: UIViewController, artistViewModelDelegate {
#IBOutlet weak var artwork: UIImageView!
#IBOutlet weak var artistName: UILabel!
#IBOutlet weak var albumName: UILabel!
func loadArtistViewModel(data: ArtistViewModel) {
guard let artistData = data as? ArtistViewModel else {}
artistName.text = artistData.artistName //Unexpectedly found nil while unwrapping an Optional value
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Hope you guys can help me on this one, Thank you so much!
EDIT
ViewController where the instance of ArtistViewController gets called
import UIKit
import Alamofire
import SwiftyJSON
protocol artistViewModelDelegate {
func loadArtistViewModel(data: ArtistViewModel)
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, artistSearchDelegate {
#IBOutlet weak var tableView: UITableView!
var data: [ArtistItem] = []
var delegate: artistViewModelDelegate?
var artistViewModel: ArtistViewModel?
var params = [API_CONSTANTS.URL_TYPES.PARAMETERS.TERM: "Maroon 5",API_CONSTANTS.URL_TYPES.PARAMETERS.COUNTRY: API_CONSTANTS.AU, API_CONSTANTS.URL_TYPES.PARAMETERS.MEDIA: API_CONSTANTS.URL_TYPES.PARAMETERS.MUSIC]
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = ArtistViewController()
getItunesData()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "artistCell", bundle: nil), forCellReuseIdentifier: "artistCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "artistCell", for: indexPath) as! artistCell
cell.artistName.text = data[indexPath.row].artistName
cell.albumName.text = data[indexPath.row].albumName
cell.genre.text = data[indexPath.row].genre
cell.trackPrice.text = "$\(String(data[indexPath.row].trackPrice))"
cell.albumArtwork.load(url: data[indexPath.row].artwork)
cell.layer.cornerRadius = 5
cell.layer.masksToBounds = true
return cell
}
//Mark: To Artist ViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
artistViewModel = ArtistViewModel(artist: data[indexPath.row])
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
delegate?.loadArtistViewModel(data: artistViewModel!)
self.navigationController?.pushViewController(artistViewController, animated: true)
}
func getItunesData(){
Alamofire.request(API_CONSTANTS.URL_TYPES.URL, method: .get, parameters: params).responseJSON
{ response in
if response.result.isSuccess {
let json = JSON(response.result.value)
self.data = ArtistModel(json: json).artistItems
self.tableView.reloadData()
} else {
}
}
}
func didTapSearch(artist: String) {
params = [API_CONSTANTS.URL_TYPES.PARAMETERS.TERM:"\(artist)"]
getItunesData()
}
#IBAction func searchButton(_ sender: Any) {
let popupSearchVC = storyboard?.instantiateViewController(withIdentifier: "popupSearchView") as! PopupViewController
popupSearchVC.delegate = self
present(popupSearchVC, animated: true, completion: nil)
}
}
The problem is the protocol. You set delegate to an instance which is not the instance in the storyboard.
But with the given code you don't need the protocol at all.
Delete
protocol artistViewModelDelegate {
func loadArtistViewModel(data: ArtistViewModel)
}
...
var delegate: artistViewModelDelegate?
...
self.delegate = ArtistViewController()
and the protocol conformance, then replace
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
artistViewModel = ArtistViewModel(artist: data[indexPath.row])
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
delegate?.loadArtistViewModel(data: artistViewModel!)
self.navigationController?.pushViewController(artistViewController, animated: true)
}
with
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController
artistViewController.artistViewModel = ArtistViewModel(artist: data[indexPath.row])
self.navigationController?.pushViewController(artistViewController, animated: true)
}
And in ArtistViewController you have to use a temporary variable for the model because the outlets are not connected (yet) right after instantiation.
Replace the code in the question with
import UIKit
class ArtistViewController: UIViewController, artistViewModelDelegate {
#IBOutlet weak var artwork: UIImageView!
#IBOutlet weak var artistName: UILabel!
#IBOutlet weak var albumName: UILabel!
var artistViewModel : ArtistViewModel!
override func viewDidLoad() {
super.viewDidLoad()
artistName.text = artistViewModel.artistName
}
}

Second Level UITableView not working in SWIFT

I've got a UITableView like so:
import UIKit
import SDWebImage
class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var ConversationList: UITableView!
let apiService = APIService()
var conversations: [Conversation] = []
var selectedConversation: Int?
override func viewDidLoad() {
super.viewDidLoad()
ConversationList.separatorStyle = .none
ConversationList.dataSource = self
ConversationList.delegate = self
// load Conversations
self.apiService.getConversations(completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
print("NUMBER OF CONVERSATIONS: ", conversations.count)
self.conversations = conversations
self.ConversationList.reloadData()
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.conversations.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatViewCell", for: indexPath) as! ChatViewCellController
cell.UserName.text = self.conversations[indexPath[1]].participants![1].username
let imgURL = URL(string: self.conversations[indexPath[1]].participants![1].profileimage!)
cell.UserLogo.sd_setImage(with: imgURL, placeholderImage: UIImage(named: "icon.turq.png"))
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected row number ", indexPath)
self.selectedConversation = indexPath[1]
self.performSegue(withIdentifier: "ChatToChatDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! ChatDetailViewController
destination.name = self.conversations[self.selectedConversation!].participants![1].username!
destination.img = self.conversations[self.selectedConversation!].participants![1].profileimage!
}
}
The UITableViewCell sits in a separate, simple class like so:
import UIKit
class ChatViewCellController: UITableViewCell {
#IBOutlet weak var UserLogo: UIImageView!
#IBOutlet weak var UserName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.UserLogo.clipsToBounds = true
self.UserLogo.layer.cornerRadius = self.UserLogo.frame.size.width / 2
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
As shown above, selecting a specific cell (i.e conversation) in the UITableView loads another UIViewController via segue. That UIViewController then contains another UITableView:
import UIKit
import SDWebImage
class ChatDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var UserLogo: UIImageView!
#IBOutlet weak var UserName: UILabel!
#IBOutlet weak var MessageList: UITableView!
var name: String = ""
var img: String = ""
let apiService = APIService()
var messages: [ChatMessage] = []
override func viewDidLoad() {
super.viewDidLoad()
// prepare header section:
self.MessageList.separatorStyle = .none
let imgURL = URL(string: self.img)
self.UserLogo.sd_setImage(with: imgURL, placeholderImage: UIImage(named: "icon.turq.png"))
self.UserLogo.clipsToBounds = true
self.UserLogo.layer.cornerRadius = self.UserLogo.frame.size.width / 2
self.UserName.text = self.name
// TODO: load messages
self.MessageList.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MessageViewCell", for: indexPath) as! ChatMessageViewCellController
cell.ChatMessageText.text = "foo"
return cell
}
}
The UITableViewCell for the above UITableView is again sitting in a separate, simple class:
import UIKit
class ChatMessageViewCellController: UITableViewCell {
#IBOutlet weak var ChatMessageBubble: UIView!
#IBOutlet weak var ChatMessageText: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
print("I'm awake")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Everything works fine up until a certain point. The first Table View loads and shows all Chats. Clicking on a Chat loads the new View with the header section, showing the Logo and Name of the Chat partner at the top of the screen and a Table underneath it. However, that table does not contain anything. To my understanding, with this code, it should show 10 rows saying "foo" and print 10 times "I'm awake" to the console.
What am I missing or doing wrong here?
In your second ViewController, you are not setting UITableViewDelegate and UITableViewDataSource. What you need to add to viewDidLoad:
MessageList.delegate = self
MessageList.dataSource = self
Bonus, for future change self.selectedConversation = indexPath[1] in first view controller to prevent bug - it will always take second model, even if you have only one.
Also, try to avoid upper case for attributes, e.g. MessageList -> messageList.

UITableview cells aren't showing

UITableview is visible while the cells aren't.
This is for a food ordering app, and I'm trying to display the menu. I've tried everything, no error has shown, but the cells ain't visible
import UIKit
import FirebaseDatabase
import FirebaseCore
class MenuVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var menu = [Food]()
var ref: DatabaseReference?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
ref = Database.database().reference()
loadMenu()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "menuCell") as? MenuCell {
let foodItem = menu[indexPath.row]
cell.configCell(food: foodItem)
return cell
}else{
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "popup", sender: menu[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let popupVC = segue.destination as? MenuPopUpVC {
if let foodItem = sender as? Food{
popupVC.config(food: foodItem)
}
}
}
func loadMenu() {
ref?.child("menu").observe(.value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let foodName = dict["name"] as! String
print(foodName)
let foodPrice = dict["price"] as! String
let foodImg = dict["image"] as! String
let foodItem = Food(name: foodName, price: foodPrice, img: foodImg)
self.menu.append(foodItem)
}
})
}
}
import UIKit
import SDWebImage
class MenuCell: UITableViewCell {
#IBOutlet weak var PriceLbl: UILabel!
#IBOutlet weak var menuImg: UIImageView!
#IBOutlet weak var menuItemLbl: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configCell(food : Food) {
let name = food.name ?? ""
menuItemLbl.text = name
let price = food.price ?? ""
PriceLbl.text = "$\(price)"
menuImg.sd_setImage(with: URL(string: food.img!)) {[weak self] (image, error, cachetype, url) in
if error == nil{
self?.menuImg.image = image
}
}
}
}
You don't reload data of your TableView, reload them after you append all foods to menu array (means after foreach loop)
ref?.child("menu").observe(.value, with: { (snapshot) in
for child in snapshot.children {
...
self.menu.append(foodItem)
}
self.tableView.reloadData()
})
You need to reload the tableView after you fill the array
self.menu.append(foodItem)
}
self.tableView.reloadData()
Also inside cellForRowAt , it's a good practice to
let cell = tableView.dequeueReusableCell(withIdentifier: "menuCell") as! MenuCell
without the misleading return UITableViewCell()

Swift 4 Switch relate to label on tableView

I have problem to get label from cell when i turn my switch ON. I do fetch all labels from Firebase Database.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagCell", for: indexPath) as! TagsTableViewCell
print(myCallList[indexPath.row])
let _tag = myCallList[indexPath.row]
cell.tagLabel?.text = _tag.type
return cell
}
UPDATED:
UITableViewCell contain nothing special
import UIKit
class TagsTableViewCell: UITableViewCell {
#IBOutlet weak var tagLabel: UILabel!
#IBOutlet weak var tagSwitch: UISwitch!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
My model:
class Calls: NSObject {
var type: String?
init(type: String?) {
self.type = type
}
}
LoadCalls contain Firebase data fetch:
func LoadCalls() {
ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
self.myCallList.removeAll()
ref.child("tags").observe(.childAdded, with: { (snapshot) in
if snapshot != nil{
var tagType = snapshot.key as? String
let myCalls = Calls(type: tagType)
self.myCallList.append(myCalls)
print(self.myCallList.count)
DispatchQueue.main.async {
self.tagsTableView.reloadData()
}
}
})
}
A delegate / protocol for communication between cell and table controller can work well here.
protocol switchCellDelegate : Class {
func cellSwitchChanged( value: String, sender: Any)
}
update table view cell with property and IBAction for switch change
class TagsTableViewCell: UITableViewCell {
weak var delegate : switchCellDelegate?
#IBAction func switchChanged(sender: UISwitch){
guard let delegate = delegate else { return }
if sender.isOn {
delegate.cellSwitchChanged( value: tagLabel.text, sender: self)
}
}
and then in cellForRowAtIndex, add this
cell.delegate = self
and controller
extension myController : switchCellDelegate {
func cellSwitchChanged( value: String, sender: Any){
//do what you want here
}
}
I guess it something like this - add tag to switcher and create action for it
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagCell", for: indexPath) as! TagsTableViewCell
print(myCallList[indexPath.row])
let _tag = myCallList[indexPath.row]
cell.tagLabel?.text = _tag.type
cell.switcher.tag = indexPath.row
return cell
}
And after this
#IBAction func switcherChanged(_ sender: UISwitch) {
var getLabel = myCallList[(sender as AnyObject).tag]
print(getLabel.type)
}

How to save the state of the checkbox to core data in Swift?

**I'm not a good English speaker. Please forgive me for my awkward English.
I'm working on a to do list project using checkbox. I cannot find a way to save the state of the checkbox to core data
This is a part of the code I use right now. Tasksis the Entity (class definition) and it has isMarked as a Boolean Attribute.
(I cut a lot to make it simple so if you find something strange in the code please write a comment)
import UIKit
import CoreData
var toDolist: [Tasks] = []
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(toDoTable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBOutlet weak var toDoTable: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return toDolist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomTableViewCell
cell.box.setImage(#imageLiteral(resourceName: "uncheckedbox"), for: .normal)
let task = toDolist[indexPath.row]
return cell
}
func getData(){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
toDolist = try context.fetch(Tasks.fetchRequest())
}catch{
print("fetching failed")
}
}
override func viewWillAppear(_ animated: Bool) {
getData()
toDoTable.reloadData()
}
}
class CustomTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBOutlet weak var box: CheckBox!
#IBOutlet weak var taskLbl: UILabel!
}
class CheckBox{
var isChecked: Bool = false{
didSet{
if isChecked == true{
self.setImage(#imageLiteral(resourceName: "checkedbox"), for: .normal)
}else{
self.setImage(#imageLiteral(resourceName: "uncheckedbox"), for: .normal)
}
}
}
override func awakeFromNib(){
self.addTarget(self, action: #selector(self.buttonClicked(_:)), for: .touchUpInside)
self.isChecked = false
}
func buttonClicked(_ sender: UIButton){
if sender == self{
if isChecked == true{
isChecked = false
}else{
isChecked = true
}
}
}
}
How can I solve this by adding some code to it? Or do I have to change all the code above?
Simple solution:
Remove the subclass of UIButton and revert the class of the button to UIButton.
In Interface Builder assign the images checkedbox and uncheckedbox to the button for states selected and default. The images are displayed automatically depending on the isSelected property of the button. That avoids completely to update the images in code.
The class CustomTableViewCell got the property task and the IBAction which must be connected to Touch Up Inside of the button. The property isSelected of the task must be changed to the name of the attribute of the NSManagedObject
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var box: UIButton!
#IBOutlet weak var taskLbl: UILabel!
var task : Tasks! {
didSet {
box.isSelected = task.isSelected
taskLbl.text = // update the label
}
}
#IBAction func buttonClicked(_ sender: UIButton)
{
let selected = !sender.isSelected
sender.isSelected = selected
task.isSelected = selected
// save the context if needed
}
}
In cellForRowAt just add the line to assign the task
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomTableViewCell
let task = toDolist[indexPath.row]
cell.task = task
return cell
}