Where to initialize struct for multiple instances in UITableViewController - ios

I'm making a task-sharing app where you can share multiple lists of tasks with multiple individuals (like one with your spouse, one with your kid, etc.). Each list's data source is a struct called TaskList, so I will have multiple instances of TaskList. Where should I initialize those instances?
I'm new at this and any help would be greatly appreciated!
This is the code for the UITableViewController that creates the task list using struct TaskList:
import UIKit
class LoLFirstTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
#IBAction func cancelToLoLFirstTableViewController(_ segue:UIStoryboardSegue) {
}
#IBAction func saveAddTask(_ segue:UIStoryboardSegue) {
if let AddTaskTableViewController = segue.source as? AddTaskTableViewController {
if let task = AddTaskTableViewController.task {
tasks.append(task)
let indexPath = IndexPath(row: tasks.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(tasks[indexPath.row].completed)
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
var tappedItem = tasks[indexPath!.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath!.row] = tappedItem
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
}
}
This is the code for TaskList:
import UIKit
struct TaskList {
var buddy: String
var phoneNumber: String
var tasks: [Task]
}

First option. You can create some kind of manager and store that list in that object:
class TaskListsManager
{
var taskLists:[TaskList] = []
}
And then pass instance of that object between your viewcontrollers:
let taskListsManager = TaskListsManager()
firstViewController.taskListsManager = taskListsManager
secondViewController.taskListsManager = taskListsManager
Second option would be to create a singleton object:
class TaskListsManager
{
static let sharedInstance = TaskListsManager()
var taskLists:[TaskList] = []
}
And you can use it anywhere in your app:
let first = TaskListsManager.sharedInstance.taskLists.first

Related

How can I take the sum of table view cells' data in Swift?

I'm trying to add up all of the amount data and send to a different table view cell. I think I need to convert String to Double before I can do this, but I'm not sure how to do this either. Does anyone know how you can take the sum of data and present it somewhere else? I'm very new to swift and am having trouble figuring out where I need to write this code.
import Foundation
struct Income: Codable {
var name: String
var amount: String
init(name: String, amount: String) {
self.name = name
self.amount = amount
}
static let DocumentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("incomes").appendingPathExtension("plist")
static func loadSampleIncomes() -> [Income] {
return [
Income(name: "Main Income", amount: "0"),
Income(name: "Secondary Income", amount: "0"),
Income(name: "Interest Income", amount: "0")]
}
static func saveToFile(incomes: [Income]) {
let propertyListEncoder = PropertyListEncoder()
let codedIncomes = try? propertyListEncoder.encode(incomes)
try? codedIncomes?.write(to: ArchiveURL, options: .noFileProtection)
}
static func loadFromFile() -> [Income]? {
guard let codedIncomes = try? Data(contentsOf: ArchiveURL) else { return nil }
let propertyListDecoder = PropertyListDecoder()
return try? propertyListDecoder.decode(Array<Income>.self, from: codedIncomes)
}
}
import UIKit
class IncomeTableViewController: UITableViewController {
var incomes: [Income] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
if let savedIncomes = Income.loadFromFile() {
incomes = savedIncomes
} else {
incomes = Income.loadSampleIncomes()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
// table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return incomes.count
} else {
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "incomeCell", for: indexPath) as! IncomeTableViewCell
let income = incomes[indexPath.row]
cell.update(with: income)
cell.showsReorderControl = true
return cell
}
// MARK: - Table view delegate
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .delete
}
override func tableView(_ tableView: UITableView, commit
editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath:
IndexPath) {
if editingStyle == .delete {
incomes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: . automatic)
Income.saveToFile(incomes: incomes)
}
}
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
let movedIncome = incomes.remove(at: fromIndexPath.row)
incomes.insert(movedIncome, at: to.row)
tableView.reloadData()
}
#IBAction func editButtonTapped(_ sender: UIBarButtonItem) {
let tableViewEditingMode = tableView.isEditing
tableView.setEditing(!tableViewEditingMode, animated: true)
}
// Navigation
#IBAction func unwindToIncomeTableView(segue:UIStoryboardSegue) {
guard segue.identifier == "saveIncomeUnwind",
let sourceViewController = segue.source as? AddEditIncomeTableViewController,
let income = sourceViewController.income else { return }
if let selectedIndexPath = tableView.indexPathForSelectedRow {
incomes[selectedIndexPath.row] = income
tableView.reloadRows(at: [selectedIndexPath],
with: .none)
} else {
let newIndexPath = IndexPath(row: incomes.count, section: 0)
incomes.append(income)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
Income.saveToFile(incomes: incomes)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditIncome" {
let indexPath = tableView.indexPathForSelectedRow!
let income = incomes[indexPath.row]
let navController = segue.destination as! UINavigationController
let addEditIncomeTableViewController = navController.topViewController as! AddEditIncomeTableViewController
addEditIncomeTableViewController.income = income
}
}
}
import UIKit
class AddEditIncomeTableViewController: UITableViewController {
#IBOutlet weak var saveButton: UIBarButtonItem!
#IBOutlet weak var incomeNameTextField: UITextField!
#IBOutlet weak var incomeAmountTextField: UITextField!
var income: Income?
override func viewDidLoad() {
super.viewDidLoad()
if let income = income {
incomeNameTextField.text = income.name
incomeAmountTextField.text = income.amount
}
updateSaveButtonState()
}
override func prepare(for segue: UIStoryboardSegue, sender:
Any?) {
super.prepare(for: segue, sender: sender)
guard segue.identifier == "saveIncomeUnwind" else { return }
let name = incomeNameTextField.text ?? ""
let amount = incomeAmountTextField.text ?? ""
income = Income(name: name, amount: amount)
}
func updateSaveButtonState() {
let nameText = incomeNameTextField.text ?? ""
let amountText = incomeAmountTextField.text ?? ""
saveButton.isEnabled = !nameText.isEmpty && !amountText.isEmpty
}
#IBAction func textEditingChanged(_ sender: UITextField) {
updateSaveButtonState()
}
}
this is the table view controller in which I want the new data to be presented.
import UIKit
class BudgetHomeTableViewController: UITableViewController {
var incomes: [Income] = []
var expenses: [Expense] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
if let savedIncomes = Income.loadFromFile() {
incomes = savedIncomes
} else {
incomes = Income.loadSampleIncomes()
}
if let savedExpenses = Expense.loadFromFile() {
expenses = savedExpenses
} else {
expenses = Expense.loadSampleExpenses()
}
}
}
This is the cell where the data will be presented specifically
import UIKit
class BugetBalanceCell: UITableViewCell {
#IBOutlet weak var budgetBalanceText: UILabel!
var incomes: [Income] = []
var expenses: [Expense] = []
override func awakeFromNib() {
super.awakeFromNib()
let incomeTotal = incomes.map({Double($0.amount) ?? 0}).reduce(0, +)
let expenseTotal = expenses.map({Double($0.amount) ?? 0}).reduce(0, +)
let balance = incomeTotal - expenseTotal
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
First define your income,expenses arrays and load data
var incomes: [Income] = []
var expenses: [Income] = []
if let savedIncomes = Income.loadFromFile() {
incomes = savedIncomes
} else {
incomes = Income.loadSampleIncomes()
}
//do the same thing for load data to expenses array
you can get your balance = incomes - expenses using
let incomeTotal = incomes.map({Double($0.amount) ?? 0}).reduce(0, +)
let expenseTotal = expenses.map({Double($0.amount) ?? 0}).reduce(0, +)
let balance = incomeTotal - expenseTotal
use this value where you want to show in next Table View conntroller
(income as NSArray).valueForKeyPath("#sum.self")

Swift 3 - Setting variable in TableViewController swift file depending on cell clicked

I'm trying to set the a string depending on which cell in a tableView is clicked. The BlueLineTableViewController is the one which should capture the user's click.
import UIKit
class BlueLineTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bluelinestations.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bluelinecell", for: indexPath)
let station = bluelinestations[indexPath.row]
cell.textLabel?.text = station.name
cell.imageView?.image = UIImage(named: station.image)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row
if row == 0 {
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if row == 1 {
BlueBelmontTableViewController().feed="http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
The BlueBelmontTableViewController's feed variable should change/be set to another url depending on which cell is clicked in the BlueLineTableViewController.
import UIKit
class BlueBelmontTableViewController: UITableViewController {
class Destinations {
var destination: String = ""
var time: String = ""
}
var feed = ""
var dataAvailable = false
var records = [Destinations]()
override func viewDidLoad() {
super.viewDidLoad()
parseData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
for r in records {
r.time = ""
r.destination = ""
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataAvailable ? records.count : 15
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (dataAvailable) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let destinationRow = records[indexPath.row]
cell.textLabel?.text = destinationRow.destination
cell.detailTextLabel?.text = destinationRow.time
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlaceholderCell", for: indexPath)
return cell
}
}
func parseData() {
guard let feedURL = URL(string: feed) else {
return
}
let request = URLRequest(url: feedURL)
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
if error != nil
{
print("Error")
}
else {
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: []) as? [String:Any] ?? [:]
print(json)
if let ctattimetable = json["ctatt"] as? [String:Any] {
if let estArrivalTime = ctattimetable["eta"] as? [[String:Any]] {
for item in estArrivalTime{
if let headingTowards = item["destNm"] as? String,
let arrivalTime = item["arrT"] as? String {
let record = Destinations()
record.destination = headingTowards
record.time = arrivalTime
self.records.append(record)
}
self.dataAvailable = true
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
catch {
}
}
}
}
task.resume()
}
}
I've tried setting the url in the didSelectRowAt method depending on the indexPath.row as can be seen in BlueLineTableViewController, but it does not seem to do anything. Does anybody know how I would go about doing this?
Below is the Main.storyboard of this part of my project:
Your are not able to pass value because you are setting feed property to the completely new instance of BlueBelmontTableViewController not the one that is added in navigation stack using your segue that you have created from your UITableViewCell to BlueBelmontTableViewController.
What you need to do is override prepareForSegue in your BlueLineTableViewController to pass your value to BlueBelmontTableViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! BlueBelmontTableViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
if indexPath.row == 0 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if indexPath.row == 1 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
}
instead of
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
use
self.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
beacause BlueBelmontTableViewController() is initialing new instance of BlueBelmontTableViewController and you want to change the instance you already have so you should use self instead of creating new instance.

Checkbox UIButton title not toggling when tapped

This code is for a tableViewController that lists tasks. When the UIButton is tapped, it's supposed to toggle the button's title from an empty string to a check mark. For some reason when I tap the button in the simulator, nothing happens and there are no errors showing in the console. Anyone know why it's not toggling? The reference code is below. Any help would be greatly appreciated! Thanks everybody!
Here's the UITableViewController code:
import UIKit
class LoLFirstTableViewController: UITableViewController {
var tasks:[Task] = taskData
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
#IBAction func cancelToLoLFirstTableViewController(_ segue:UIStoryboardSegue) {
}
#IBAction func saveAddTask(_ segue:UIStoryboardSegue) {
if let AddTaskTableViewController = segue.source as? AddTaskTableViewController {
if let task = AddTaskTableViewController.task {
tasks.append(task)
let indexPath = IndexPath(row: tasks.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
var rowChecked: [Bool] = Array(repeating: false, count: tasks.count)
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(rowChecked[indexPath.row])
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
var tappedItem = tasks[indexPath!.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath!.row] = tappedItem
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
}
Here's the code for the UIButton:
import UIKit
class CheckButton : UIButton {
convenience init() {
self.init(frame:CGRect.init(x: 0, y: 0, width: 20, height: 20))
self.layer.borderWidth = 2
self.layer.cornerRadius = 10
self.titleLabel?.font = UIFont(name:"Georgia", size:10)
self.setTitleColor(.black, for: .normal)
self.check(false)
}
func check(_ yn:Bool) {
self.setTitle(yn ? "✔" : "", for: .normal)
}
override init(frame:CGRect) {
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You only every call check with cb.check(rowChecked[indexPath.row]) and rowChecked is always an array of [false, false, false, ...].
This should probably be cb.check(tasks[indexPath.row].completed) based on what you're doing in buttonTapped.

Value of type '_' has no member

In the cb.check(self.rowChecked[indexPath.row]) line under cellForRowAt I'm getting a "Value of type 'LolFirstTableViewController' has no member 'rowChecked'" even though I set up rowChecked to be an array of Booleans with tasks.count number of items. Do I need to initialize rowChecked somewhere else besides cellForRowAt or what am I doing wrong here? The point of this code is to make a checkbox show up in each cell of a table where you can click it to change the accessory to a check mark, and click it again to uncheck it. The check box itself is a separate custom class called CheckButton. I'm still learning Swift so any help would be greatly appreciated! Thank you!
import UIKit
class LoLFirstTableViewController: UITableViewController {
var tasks:[Task] = taskData
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
#IBAction func cancelToLoLFirstTableViewController(_ segue:UIStoryboardSegue) {
}
#IBAction func saveAddTask(_ segue:UIStoryboardSegue) {
if let AddTaskTableViewController = segue.source as? AddTaskTableViewController {
if let task = AddTaskTableViewController.task {
tasks.append(task)
let indexPath = IndexPath(row: tasks.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
var rowChecked: [Bool] = Array(repeating: false, count: tasks.count)
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(self.rowChecked[indexPath.row])
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
var tappedItem = tasks[indexPath!.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath!.row] = tappedItem
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
}
You are declaring rowChecked as a local variable and calling it with self.rowChecked as if it were a class property.
To solve this issue, remove the self. before rowChecked.
Old:
cb.check(self.rowChecked[indexPath.row])
New:
cb.check(rowChecked[indexPath.row])
There might be further issues, but that's the reason for the error as your code currently stands.
You have the line: var rowChecked: [Bool] = Array(repeating: false, count: tasks.count) inside the tableView:cellForRowAt method, so it's a local variable, it's not a property of the LolFirstTableViewController class.
That means you need to change this line: cb.check(self.rowChecked[indexPath.row]) to cb.check(rowChecked[indexPath.row]) (Removed self.).

How to delete a cell in tableView by clicking a button in a cell? Using coreData

I create a ToDo List app.
I used tableView to list the tasks. And I use a custom class for cell. In cell contentView I have a label and one done button in it. I have successfully implemented the done button click action in my code. It works fine.
Problem
When I click the done button it deletes the last added task. But not the clicked one. And when I retry to click the done Button it perform no action. How to resolve this error
GIF added below, click link
Entity class ToDo
import Foundation
import CoreData
public class ToDo: NSManagedObject {
public override func awakeFromInsert() {
self.created = NSDate()
}
}
MainVC
import UIKit
import CoreData
class MainVC: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var controller: NSFetchedResultsController<ToDo>!
#IBOutlet weak var taskTextField: CustomTextField!
#IBOutlet weak var tableView: UITableView!
var toDo: ToDo!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// generateData()
attemptFetch()
}
// to give view to cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell
}
// custom function
func configureCell(cell: ItemCell, indexPath: NSIndexPath) {
let toDo = controller.object(at: indexPath as IndexPath)
// call the method on the ItemCell
cell.configureCell(toDo: toDo)
// done button click
cell.doneBtn.tag = indexPath.row
cell.doneBtn.addTarget(self, action: #selector(MainVC.donePressed), for: UIControlEvents.touchUpInside)
}
// when select a cell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// it ensure it have object and atleast one object in there
if let objs = controller.fetchedObjects, objs.count > 0 {
let task = objs[indexPath.row]
performSegue(withIdentifier: "ItemDetailsVC", sender: task)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ItemDetailsVC" {
if let destination = segue.destination as? ItemDetailsVC {
if let task = sender as? ToDo {
destination.taskDetails = task
}
}
}
}
// count of cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// we check here if any sections then take info of them and count
if let sections = controller.sections {
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
// column count
func numberOfSections(in tableView: UITableView) -> Int {
if let sections = controller.sections {
return sections.count
}
return 0
}
// give height of a cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
// fetching function
func attemptFetch() {
// create a fetch request with fetching Entity
let fetchRequest: NSFetchRequest<ToDo> = ToDo.fetchRequest()
// sorting area
let dateSort = NSSortDescriptor(key: "created", ascending: true)
fetchRequest.sortDescriptors = [dateSort]
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller
// actual fetching
do {
try controller.performFetch()
} catch {
let error = error as NSError
print("\(error)")
}
}
// when tableView changes this function starts listen for changes and
// it will handle that for you
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
// this function will listen for when we make change
// insertion, deletion .. etc
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case.insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
case.delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break
case.update:
if let indexPath = indexPath {
let cell = tableView.cellForRow(at: indexPath)
//update the cell data
configureCell(cell: cell as! ItemCell, indexPath: indexPath as NSIndexPath)
}
break
case.move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
}
}
#IBAction func addBtnPressed(_ sender: UIButton) {
if taskTextField.text != "" && taskTextField.text != nil {
toDo = ToDo(context: context)
if let task = taskTextField.text {
toDo.title = task
}
ad.saveContext()
taskTextField.text = ""
self.tableView.reloadData()
}
}
// done button
func donePressed() {
if toDo != nil {
context.delete(toDo)
ad.saveContext()
}
}
func generateData() {
let task = ToDo(context: context)
task.title = "alwin"
let task1 = ToDo(context: context)
task1.title = "rambo"
let task2 = ToDo(context: context)
task2.title = "monisha"
let task3 = ToDo(context: context)
task3.title = "wounderlist"
let task4 = ToDo(context: context)
task4.title = "presentation"
let task5 = ToDo(context: context)
task5.title = "roundup"
// to save data
ad.saveContext()
}
}
ItemDetailsVC
import UIKit
class ItemDetailsVC: UIViewController {
var taskDetails: ToDo?
#IBOutlet weak var detailsLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// to clear the <DreamLIst to < only
if let topItem = self.navigationController?.navigationBar.topItem {
topItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
// this is execute when tap on an existing cell
if taskDetails != nil {
loadItemData()
}
}
}
func loadItemData() {
if let task = taskDetails {
detailsLbl.text = task.title
}
}
override func viewDidLayoutSubviews() {
detailsLbl.sizeToFit()
}
#IBAction func deletePressed(_ sender: UIBarButtonItem) {
if taskDetails != nil {
context.delete(taskDetails!)
ad.saveContext()
}
_ = navigationController?.popViewController(animated: true)
}
}
storyboard, click link below
ItemCell
import UIKit
class ItemCell: UITableViewCell {
#IBOutlet weak var taskTitle: UILabel!
#IBOutlet weak var doneBtn: UIButton!
var toDo: ToDo?
func configureCell(toDo: ToDo) {
taskTitle.text = toDo.title
}
}
OK Currently you are setting the selector of your done button to outside of its container (cell) this is bad practice in general, you are configuring the cell with a ToDo but not assigning the optional inside the cell, supposedly there to keep a reference to the ToDo.
In my opinion I would change this slightly so that you store the reference to the ToDo firstly:
func configureCell(toDo: ToDo) {
self.toDo = toDo
taskTitle.text = toDo.title
}
Now on your cell create a Protocol, then configure the cell with a ToDo and a delegate, then on button press tell the delegate your button was pressed with the relevant ToDo...
protocol ToDoCellDelegate: class {
func toDoCellButtonPressed(todo: ToDo?)
}
Now on your cell configure as:
func configureCell(toDo: ToDo, delegate: ToDoCellDelegate) {
self.delegate = delegate
self.toDo = toDo
taskTitle.text = toDo.title
}
and add a ref to the delegate in the cell:
weak var delegate: ToDoCellDelegate?
now change your buttons selector to a func inside the cell
func buttonPressed() {
self.delegate?.cellToDoButtonPressed(toDo: toDo)
}
Then in your VC you conform to the delegate passing self in the configuration and implement the delegate:
extension ItemDetailsVC: ToDoCellDelegate {
func toDoCellButtonPress(toDo: ToDo?) {
if let t = toDo {
//tell context to delete todo and remove cell.
}
}
}
Ok so then you should create an IBAction outlet for your button in ItemCell and then create a protocol of this form :
protocol ItemDelegate {
func clicked()
}
class ItemCell: UITableViewViewCell {
var delegate : ItemDelegate?
var indexPath: IndexPath?
//call delegate?.clicked() where you have the gesture recogniser
}
Then in cellForRowAtIndexPath
cell.delegate = self
cell.indexPath = indexPath
Then implement the extension for your class:
extension MyTableView: ItemDelegate {
func clicked(indexPath: IndexPath) {
//dismiss cell for indexPath
}
}

Resources