Saving the right row in a TableView - ios

I`m a Beginner in swift and i have tried to build a shopping list app with a tableView in it. Nearly everthing works, except:
When you reorder the rows, quit the app, and going back, the new row order was not saved.
Any ideas?
This is the important part:
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = shoppingList[sourceIndexPath.row]
shoppingList.remove(at: sourceIndexPath.row)
shoppingList.insert(movedObject, at: destinationIndexPath.row)
}
But in will not work.....
Here is the whole code:
import UIKit
import CoreData
var shoppingList: [NSManagedObject] = [ ]
class ShoppingList_1: UIViewController, UITableViewDelegate,UITableViewDataSource {
//Anzahl Spalten -->
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
//**************************
//Zeilen: dynamisch; abhängig von Anzahl items in Liste -->
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shoppingList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = shoppingList[indexPath.row]
let cell = Cell.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
// <-- Prototype Cell für Ausschnitt/Sicht des Bildschirms
cell.textLabel?.text = item.value(forKeyPath: "itemName") as? String
cell.detailTextLabel?.text = "\(indexPath.row)"
return cell
}
//free to use
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
print("Daten geladen!\n")
let movedObject = shoppingList[sourceIndexPath.row]
shoppingList.remove(at: sourceIndexPath.row)
shoppingList.insert(movedObject, at: destinationIndexPath.row)
NSLog("%#", "\(sourceIndexPath.row) => \(destinationIndexPath.row) \(shoppingList)")
// To check for correctness enable: self.tableView.reloadData()
}
//*****************************************************************************************************
//Durch wischen entfernen -->
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete
{
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let managedContext = appDelegate.persistentContainer.viewContext
managedContext.delete(shoppingList[indexPath.row])
do {
try managedContext.save()
shoppingList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
} catch let err as NSError {
print("12345", err)
}
}
}
#IBOutlet weak var AddButton: UIButton!
#IBOutlet weak var AddItem: UITextField!
#IBOutlet weak var Cell: UITableView!
#IBAction func EditTopRightButton(_ sender: UIBarButtonItem) {
self.Cell.isEditing = !self.Cell.isEditing
sender.title = (self.Cell.isEditing) ? "Done" : "Edit"
}
//Button Action
#IBAction func AddButton2_Test(_ sender: Any) {
print("My test")
let a:Float = 2
let b:Float = 3
let Ergebnis:Float = a+b
print(Ergebnis)
if (AddItem.text != "")
{
self.save(AddItem.text!)
}
Cell.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
do {
shoppingList = try managedContext.fetch(fetchRequest)
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
func save(_ itemName: String){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Item", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "itemName")
do {
try managedContext.save()
shoppingList.append(item)
} catch let err as NSError {
print("Failed to save item",err)
}
}
//*****************************************************************************************************
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Cell.delegate = self
Cell.dataSource = self
AddItem.delegate = self
}
//HINZU-->
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//*****************************************************************************************************
// extensions -->
extension ShoppingList_1 : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
AddItem.resignFirstResponder()
return true
}
}
//***************************************

Current Working
In your code, Table view is loaded with data from Coredata at viewWillAppear. Data inserted to core data is not updated when you change the order in TableView.
So even you changed order of cell and corder in Array. Every time viewWillAppear calls Data is again fetched from Coredata with no order.
ViewWillAppear will call every time view display in screen. Incase you are coming back to this view from any other presented view ViewWillAppear will call.
Sollution
You can create a new attribute in table describing order of item in Coredata Entity & Sort the item with this order after fetching.
Refer this answer to know how to set display order with core data
https://stackoverflow.com/a/31907857/4845644
Or If your requirement is just to persist order to ViewController instances existence you need to change core data fetch code from viewWillAppear to viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
Cell.delegate = self
Cell.dataSource = self
AddItem.delegate = self
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
do {
shoppingList = try managedContext.fetch(fetchRequest)
} catch let err as NSError {
print("Failed to fetch items", err)
}
}

Related

Duplicate cells on each load of tableView from coredata

The view shows duplicate rows for each record in CoreData which keep multiplying on each reload. The code is as under.
What happen is whenever I add record then I view record it shows me the record. Then I click back for the homepage after that when I click on view record I see the copy of same record. So now I have 2 same records. Can anyone please help me with and I think the problem is in table view so here is my table view controller code
import UIKit
import CoreData
var Rec = [Records]()
class TableViewController: UITableViewController {
var firstLoad = true
func nondel() -> [Records]
{
var nodellist = [Records]()
for note in Rec
{
if(note.del == nil)
{
nodellist.append(note)
}
}
return nodellist
}
override func viewDidLoad() {
super.viewDidLoad()
if(firstLoad)
{
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context:NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Records")
do{
let results: NSArray = try context.fetch(request) as NSArray
for result in results {
let note = result as! Records
Rec.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell
let thisrec: Records!
thisrec = nondel()[indexPath.row]
cell.idLB.text = thisrec.id
cell.nameLB.text = thisrec.name
cell.lastLB.text = thisrec.last
cell.genderLB.text = thisrec.gender
cell.ageLB.text = thisrec.age
cell.addressLB.text = thisrec.address
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return nondel().count
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
self.performSegue(withIdentifier: "editNote", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "editNote")
{
let indexPath = tableView.indexPathForSelectedRow!
let recDetail = segue.destination as? AddViewController
let selectedCell: Records!
selectedCell = nondel()[indexPath.row]
recDetail!.selectedCell = selectedCell
tableView.deselectRow(at: indexPath, animated: true)
}
}
}
Your code is unbelievable cumbersome.
First of all never declare a data source outside of any class.
Second of all never use a function to build an array as table view data source.
Third of all firstRun is pointless because viewDidLoad is called only once anyway.
Fourth of all rather than filtering the received records manually apply a predicate to the fetch request
Further it's highly recommended to name Core Data entities always in singular form (Record) and to use the specific generic fetch request of this entity.
class TableViewController: UITableViewController {
var records = [Record]()
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request : NSFetchRequest<Record> = Record.fetchRequest()
request.predicate = NSPredicate(format: "del != nil")
do {
records = try context.fetch(request)
} catch { print("Fetch Failed", error) }
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell
let thisrec = records[indexPath.row]
cell.idLB.text = thisrec.id
cell.nameLB.text = thisrec.name
cell.lastLB.text = thisrec.last
cell.genderLB.text = thisrec.gender
cell.ageLB.text = thisrec.age
cell.addressLB.text = thisrec.address
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return records.count
}
...

UITableViewCell public function not executing

var list = [String]()
#IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
self.title = "Routines"
TableView.delegate = self
TableView.dataSource = self
super.viewDidLoad()
}
//refresh view when going back to this viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Test Worked")
TableView.reloadData()
}
//generating rows
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
//returning text in UITableViewCell
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style:
UITableViewCell.CellStyle.default, reuseIdentifier:
"prototype1")
print("printed")
cell.textLabel?.text = list[indexPath.row]
return cell
}
//deleting rows
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == UITableViewCell.EditingStyle.delete{
deleteAllData("ToDo")
self.list.remove(at: indexPath.row)
TableView.reloadData()
}
}
#IBAction func didAdd() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "addRoutinePage")as! addRoutinePage
self.navigationController?.pushViewController(vc, animated: true)
}
//function to get data from core data
func getData()
{
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do{
//fetching data from coredata
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
//appending the list from the value in coredata (attribute) or entity
self.list.append(data.value(forKey: "title")as! String)
print("append success")
}
}catch {
print("failed")
}
}
What is wrong with my code? Everything seems to work except for the UITableViewCell, the print command I entered just to check if the function is executed didn't even work. I tried TableView.reloadData() but it still didn't work. Logically if the problem is with the public function or data source or delegate it won't even generate any rows, but rows are generated. I tried resizing the cell height size too but it still won't work. Please help!
There are a few errors with the code:
You need to reload once the data fetching from CoreData is complete.
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
self.list.append(data.value(forKey: "title")as! String)
}
self.TableView.reloadData()
} catch {
print(error)
}
}
Also, don't forget to call the getData function.
override func viewDidLoad() {
super.viewDidLoad()
title = "Routines"
TableView.delegate = self
TableView.dataSource = self
getData()
}

How can I get an array to an NSManagedObject

I am sorry if I put out silly questions but I am new to Swift. I am building an app and so far it goes quite well. But now I want to delete some rows from my tableview which gets feeded from an Array with Strings. And then I want to save/fetch that using core data. I believe I have the code for it but the problem is that I am trying to save an array full of Strings. so I get error message saying: Cannot convert value of type 'String' to expected argument type 'NSManagedObject'. And therefore I am wondering, how can I solve this? Is there any way to "add those Strings to an NSManagedObject somehow?
Here are my code:
the array :
and here are the code:
import UIKit
import CoreData
class tableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (List.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = List.self[indexPath.row]
cell.textLabel?.textColor = UIColor.white
cell.backgroundColor = UIColor.clear
return(cell)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
if editingStyle == .delete{
let rows = List[indexPath.row]
context.delete(rows)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
do{
List = try context.fetch(MealsMenu.fetchRequest()) as! [String]
}
catch{
print(error)
}
}
myTableView.reloadData()
}
#IBOutlet weak var myTableView: UITableView!
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
CoredataClass.saveItems()
CoredataClass.loadData()
}
}
You will need to create a function like this
Imagine that your Entity is Recipe which has an string attribute recipeName which contains the name of the recipe.
func fetchPersistentData() -> [String] {
var recipes = [String]()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return recipes
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<Recipe>(entityName: "Recipe")
do {
let fetchedResults = try managedContext.fetch(fetchRequest)
if fetchedResults.count > 0 {
recipes = fetchedResults.map { ("\($0.recipeName)")}
return recipes
}
} catch let error as NSError {
// Wrong handling
print(error.description)
}
return recipes
}

How to display UiDatePicker to labels in tableview cell

My project is a reminder list and has only two view controllers(vc).
The first displays the tableview using Subtile style on the tableview cell. The second vc has two text labels, the date picker and save button.
My questions;
1) How do I send the time only to the label I have created in the tableview cell?
2) When adding the date picker to xcdatamodeld (core data), under Attribute what is the Type? I have tried Double, but that didn't work.
Greatly appreciate your support.
import UIKit
class AddEventViewController: UIViewController {
#IBOutlet weak var addReminderTextField: UITextField!
#IBOutlet weak var addInformationTextField: UITextField!
var datePicker: UIDatePicker!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.isTranslucent = false
}
override func viewDidLoad() {
super.viewDidLoad()
datePicker = UIDatePicker()
datePicker.center = view.center
view.addSubview(datePicker)
}
#IBAction func cancelButtonTapped(_ sender: Any) {
navigationController!.popViewController(animated: true)
}
#IBAction func saveButtonTapped(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Task(context: context)
task.title = addReminderTextField.text!
task.subtitle = addInformationTextField.text!
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popViewController(animated: true)
}
}
And here is my tableview code...
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
let task = tasks[indexPath.row]
cell.textLabel?.text = task.title!
cell.detailTextLabel?.text = task.subtitle
return cell
}
func getTasks() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
tasks = try context.fetch(Task.fetchRequest())
}
catch {
print("Fetching Failed")
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
if editingStyle == .delete {
let task = tasks[indexPath.row]
context.delete(task)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
do {
tasks = try context.fetch(Task.fetchRequest())
}
catch {
print("Fetching Failed")
}
}
tableView.reloadData()
}
}

how to put 2 text in 1 row when you add?

import UIKit
import CoreData
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate {
#IBOutlet weak var text1: UITextField!
#IBOutlet weak var text2: UITextField!
#IBOutlet weak var ttableview: UITableView!
/////
//CoreData
func saveName(name: String) {
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Zain",in:context)
let person1 = NSManagedObject(entity: entity!,insertInto: context)
let person2 = NSManagedObject(entity: entity!,insertInto: context)
person1.setValue(name, forKey: "man1")
person2.setValue(name, forKey: "man2")
do {
try context.save()
//5
array1.insert(person1, at: 0)
array2.insert(person2, at: 0)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
/////
#IBAction func Add(_ sender: Any) {
self.saveName(name: text1.text!)
self.saveName(name: text2.text!)
self.ttableview.reloadData()
}
//deleat row and tableview and arry
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
array1.remove(at: indexPath.row)
array2.remove(at: indexPath.row)
ttableview.deleteRows(at: [indexPath], with: .fade)
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
context.delete(array1[indexPath.row] as NSManagedObject)
context.delete(array2[indexPath.row] as NSManagedObject)
do {
try context.save()
} catch _ {
print("remove object error")
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array1.count
}
func ttableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ttableview.dequeueReusableCell(withIdentifier: "cell") as! Cell
let person1 = array1[indexPath.row]
let person2 = array2[indexPath.row]
cell.lable1.text = person1.value(forKey: "man1") as! String?
cell.lable2.text = person2.value(forKey: "man2") as! String?
return cell
}
var array1 = [NSManagedObject]()
var array2 = [NSManagedObject]()
override func viewDidLoad() {
super.viewDidLoad()
ttableview.delegate = self
ttableview.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Zain")
do {
let results = try context.fetch(fetchRequest)
array1 = results as! [NSManagedObject]
array2 = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
}
Why don't you just use two labels / textFields in your tableview cells?
You will need a custom uitableviewcell for this.

Resources