My TableView only lists the last item in my itemArray? - ios

I have four items in my itemArray, but my TableView only lists my last appended item, and it lists it first. However, I can check and uncheck the three rows below the first row that actually has a text.
import UIKit
class ToDoListViewController: UITableViewController {
var itemArray = [Item]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
let newItem = Item()
newItem.title = "Find Mike"
itemArray.append(newItem)
let newItem2 = Item()
newItem.title = "Buy Eggos"
itemArray.append(newItem2)
let newItem3 = Item()
newItem.title = "Destroy Demongorgon"
itemArray.append(newItem3)
let newItem4 = Item()
newItem.title = "RockOn!"
itemArray.append(newItem4)
// if let items = defaults.array(forKey: "ToDoListArray") as? [String] {
// itemArray = items
// }
}
//MARK: - TableView Datasource Methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoItemCell", for: indexPath)
let item = itemArray[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = item.done ? .checkmark : .none
return cell
}
//MARK: - TableView Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(itemArray[indexPath.row])
itemArray[indexPath.row].done = !itemArray[indexPath.row].done
tableView.reloadData()
tableView.deselectRow(at: indexPath, animated: true)
}
//MARK: - Add New Items
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Add New Todoey Item", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
//what will happen once the user clicks the add item
let newItem = Item()
newItem.title = textField.text!
self.itemArray.append(newItem)
self.defaults.set(self.itemArray, forKey: "ToDoListArray")
self.tableView.reloadData()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}
StackOverflow say I need more of a description... I think this says it all... This is for a Udemy iOS series...

You have used same instance for set item name. Change your viewDidLoad method.
override func viewDidLoad() {
super.viewDidLoad()
let newItem = Item()
newItem.title = "Find Mike"
itemArray.append(newItem)
let newItem2 = Item()
newItem2.title = "Buy Eggos"
itemArray.append(newItem2)
let newItem3 = Item()
newItem3.title = "Destroy Demongorgon"
itemArray.append(newItem3)
let newItem4 = Item()
newItem4.title = "RockOn!"
itemArray.append(newItem4)
// if let items = defaults.array(forKey: "ToDoListArray") as? [String] {
// itemArray = items
// }
}

check this and it's working, Your code after some slight edits:
import UIKit
class Item {
var title: String
var done: Bool
init(itemTitle: String, itemStatus: Bool) {
title = itemTitle
done = itemStatus
}
}
class ToDoListViewController: UITableViewController {
var itemArray = [Item]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
addItemsToArray()
// if let items = defaults.array(forKey: "ToDoListArray") as? [String] {
// itemArray = items
// }
}
//MARK: - add Items
func addItemsToArray(){
let newItem = Item(itemTitle: "Find Mike", itemStatus: false)
itemArray.append(newItem)
let newItem2 = Item(itemTitle: "Buy Eggos", itemStatus: false)
itemArray.append(newItem2)
let newItem3 = Item(itemTitle: "Destroy Demongorgon", itemStatus: false)
itemArray.append(newItem3)
let newItem4 = Item(itemTitle: "RockOn!", itemStatus: false)
itemArray.append(newItem4)
}
//MARK: - TableView Datasource Methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoItemCell", for: indexPath)
cell.textLabel?.text = itemArray[indexPath.row].title
cell.accessoryType = itemArray[indexPath.row].done ? .checkmark : .none
return cell
}
//MARK: - TableView Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(itemArray[indexPath.row])
itemArray[indexPath.row].done = !itemArray[indexPath.row].done
tableView.reloadData()
tableView.deselectRow(at: indexPath, animated: true)
}
//MARK: - Add New Items
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Add New Todoey Item", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
//what will happen once the user clicks the add item
let newItem = Item(itemTitle: textField.text!, itemStatus: false)
self.itemArray.append(newItem)
self.defaults.set(self.itemArray, forKey: "ToDoListArray")
self.tableView.reloadData()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}

The problem in your implementation is that you are using the same instance you are appending newItem instead of newItem2 and newItem3
I would strongly advise using the below approach I will provide, it is safe and be customize easily
struct Item {
var titleName: String?
var description: String?
init(titleName: String, description: String) {
self.titleName = titleName
self.description = description
}
}
enum ItemOptions: Int, CaseIterable {
case Find
case Local
case Search
var title: String {
switch self {
case .Find: return "Find"
case .Local: return "Local"
case .Search: return "Search"
}
}
var desciprion: String {
switch self {
case .Find: return "Find Desc"
case .Local: return "Local Desc"
case .Search: return "Search Desc"
}
}
}
class ToDoListViewController: UITableViewController {
var itemArray = [Item]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
for item in ItemOptions.allCases {
itemArray.append(Item(titleName: item.title, description: item.desciprion))
}
}
}
You can update the item Struct and add multiple values and only you change the enum either add maybe a new image, price.

Related

How can I fix the error that says no value inputs to core data?

I would like to save the values "title: String" and "done: Boolean" by following sentences. Title is written in textfield in Alert, and done is set as false as default. but there are errors saying no value is input to core data.
How can I fix this?
import UIKit
import CoreData
class TodoListViewController: UITableViewController {
var itemArray = [Item]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - TableViewDataSource Methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoItemCell", for: indexPath)
let item = Item(context: context)
cell.textLabel?.text = item.title
cell.accessoryType = item.done ? .checkmark : .none
return cell
}
// MARK: - TableView Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
itemArray[indexPath.row].done = !itemArray[indexPath.row].done
tableView.deselectRow(at: indexPath, animated: true)
tableView.reloadData()
self.saveItems()
}
// MARK: - Add New Items
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Add New Todoey Item", message: "", preferredStyle: .alert)
var textField = UITextField()
let newItem = Item(context: self.context)
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
print(textField.text ?? "no data")
newItem.title = alert.textFields?.first?.text ?? ""
newItem.done = false
self.itemArray.append(newItem)
print(self.itemArray)
print(newItem)
self.saveItems()
self.tableView.reloadData()
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
func saveItems(){
do {
print("\(context)")
try context.save()
}catch {
print("error, \(error)")
}
}
Error message is as follows:
error, Error Domain=NSCocoaErrorDomain Code=1570 "title is a required value." UserInfo={NSValidationErrorObject=<Todoey.Item: 0x600000424a00> (entity: Item; id: 0x60000277c4c0 x-coredata:///Item/tA47CD9C0-9813-49C7-9594-91AFFCFE511F3; data: {
done = 0;
title = nil;
}), NSLocalizedDescription=title is a required value., NSValidationErrorKey=title, NSValidationErrorValue=null}
It is the first time to ask a question here. If there is more detail required, please let me know.
I write in the textfield, but there is no text in tableView. but when I print , there is a right input.
how app works
code of appdelegate
delegate properties in viewcontroller

Edit operation is not working in Core Data

I'm working on core data crud operation.I've Person object and when user click on add (+) button user can add the person name and person name will show in tableview. When user click on any tableView cell it shows the alert and current tap cell person show in alert text field and user can edit the name of person but my logic is not working fine and user is not editing.I've one more problem like when i run the app the pervious users record not showing but it will showing after I will enter the new user and fetch all the pervious users see the code and guide me thanks in advance.
ViewController Class:
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items : [Person] = [] {
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: StundentCell.self), bundle: .main), forCellReuseIdentifier: String(describing: StundentCell.self))
}
#IBAction func addPerson(_ sender: UIBarButtonItem) {
showAlert()
}
private func showAlert(_ title:String = "Add new Item", message: String = "what is your name"){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addTextField()
let subbmitBtn = UIAlertAction(title: "Add Person", style: .default) { (action) in
let textField = alertController.textFields![0]
let newPerson = Person(context:self.context)
newPerson.name = textField.text
newPerson.age = 20
newPerson.gender = "Male"
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(subbmitBtn)
self.present(alertController, animated: true, completion: nil)
}
func fetchPerople(){
do{
self.items = try context.fetch(Person.fetchRequest())
}
catch{
}
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.configurePersonCell(tableView, cellForRowAt: indexPath)
}
func configurePersonCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StundentCell.self)) as? StundentCell else {return UITableViewCell()}
cell.studentData = items[indexPath.row]
cell.delete.tag = indexPath.row
cell.delete.addTarget(self, action: #selector(deleteRecord(sender:)), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var person = items[indexPath.row].name
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
let getName = alertController.textFields![0]
person = getName.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}
func gotoPersonDetails(personData:Person){
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
#objc func deleteRecord(sender: UIButton){
if items.count > 0{
let personRemove = self.items[sender.tag]
self.context.delete(personRemove)
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
}
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items : [Person] = [] {
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: StundentCell.self), bundle: .main), forCellReuseIdentifier: String(describing: StundentCell.self))
}
#IBAction func addPerson(_ sender: UIBarButtonItem) {
showAlert()
}
private func showAlert(_ title:String = "Add new Item", message: String = "what is your name"){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addTextField()
let subbmitBtn = UIAlertAction(title: "Add Person", style: .default) { (action) in
let textField = alertController.textFields![0]
let newPerson = Person(context:self.context)
newPerson.name = textField.text
newPerson.age = 20
newPerson.gender = "Male"
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(subbmitBtn)
self.present(alertController, animated: true, completion: nil)
}
func fetchPerople(){
do{
self.items = try context.fetch(Person.fetchRequest())
}
catch{
}
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.configurePersonCell(tableView, cellForRowAt: indexPath)
}
func configurePersonCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StundentCell.self)) as? StundentCell else {return UITableViewCell()}
cell.studentData = items[indexPath.row]
cell.delete.tag = indexPath.row
cell.delete.addTarget(self, action: #selector(deleteRecord(sender:)), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var person = items[indexPath.row].name
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
//get the person name in alert text field
let getName = alertController.textFields![0]
//edit the name of the person object
person = getName.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}
func gotoPersonDetails(personData:Person){
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
#objc func deleteRecord(sender: UIButton){
/* fro api calling or firebase for deleting the rows in tableview
if items.count > 0 {
items.remove(at:sender.tag)
}
*/
if items.count > 0{
let personRemove = self.items[sender.tag]
//remove the person
self.context.delete(personRemove)
//save the data
do {
try self.context.save()
}
catch{
}
//refetch the data
self.fetchPerople()
}
}
You are modifying the name string but you don't update the record.
Get the whole person
let person = items[indexPath.row] // can be a constant because it's reference type
assign the name to the text field
alertField.text = person.name
and assign the edited name back
let nameField = alertController.textFields![0]
person.name = nameField.text
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let person = items[indexPath.row]
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person.name
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
let nameField = alertController.textFields![0]
person.name = nameField.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}

How to save UILabel from custom cell so that every time I open the app it displays the latest change?

I am a beginner in Swift. I've exhausted all my trial and errors and need help!!
I am creating a scoreboard project using a UITableView with a Custom Cell that holds a UILabel and a UIButton. After a button press the UILabel increments by one to simulate a point for the player. I am having trouble saving the point in UILabel so that every time I open the app the point for that player remains. I've tried using UserDefaults, structs, and delegates but have't had any luck...I could be doing it wrong. I just need to know what the proper approach is for this.
Note: I am able to save the player name successfully from the UIAlertController so that when I open the app the names are still there unless I delete them, but haven't had any luck saving the points for each name itself, they still remain "0".
It should look like this when I close then open the app, but it only does this when the app is opened:
Scoreboard UITableView - Screenshot
Here's the ViewController code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var items = [String]()
#IBOutlet weak var listTableView: UITableView!
#IBAction func addItem(_ sender: AnyObject) {
alert()
}
override func viewDidLoad() {
super.viewDidLoad()
listTableView.dataSource = self
self.items = UserDefaults.standard.stringArray(forKey:"items") ?? [String]()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! PointsCell
cell.textLabel?.text = items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func saveData() {
UserDefaults.standard.set(items, forKey: "items")
}
func alert(){
let alert = UIAlertController(title: "Add Player", message: "", preferredStyle: .alert)
alert.addTextField{
(textfield) in
textfield.placeholder = " Enter Player Name "
}
let add = UIAlertAction(title: "Add", style: .default)
{
(action) in guard let textfield = alert.textFields?.first else {return}
if let newText = textfield.text
{
self.items.append(newText)
self.saveData()
let indexPath = IndexPath(row: self.items.count - 1, section: 0)
self.listTableView.insertRows(at: [indexPath], with: .automatic)
}
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) {
(alert) in
}
alert.addAction(add)
alert.addAction(cancel)
present(alert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
saveData()
}
}
Here is my custom cell code called PointsCell:
import UIKit
class PointsCell: UITableViewCell {
var winScore = 0
#IBOutlet weak var scoreUILabel: UILabel!
#IBAction func pointButtonPressed(_ sender: Any) {
winScore += 1
scoreUILabel.text = "\(winScore)"
}
}
The points still remains 0 because you are note saving them, firstly you need points for each player so you need to combine player name and score into object.
class Player:NSObject, Codable{
let name: String
var score : Int
init(name: String, score: Int) {
self.name = name
self.score = score
}
override var description: String{
return "Name :" + self.name + " Score :" + String(self.score )
}
}
Swift 4 introduced the Codable protocol, by adopting Codable on your own types enables you to serialize them to and from any of the built-in data formats.
Now you can easily access a player with name and score.
var players = [Player]()
To get stored value from UserDefaults
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let items: Data = UserDefaults.standard.object(forKey: "items") as? Data{
self.players = try! PropertyListDecoder().decode([Player].self, from: items)
self.listTableView.reloadData()
}
}
When you adding new player to your list create new instance of Player and add it to list.
func alert(){
let alert = UIAlertController(title: "Add Player", message: "", preferredStyle: .alert)
alert.addTextField{
(textfield) in
textfield.placeholder = " Enter Player Name "
}
let add = UIAlertAction(title: "Add", style: .default)
{
(action) in guard let textfield = alert.textFields?.first else {return}
if let newText = textfield.text
{
let player = Player(name: newText, score: 0)
self.players.append(player)
let indexPath = IndexPath(row: self.players.count - 1, section: 0)
self.listTableView.insertRows(at: [indexPath], with: .automatic)
}
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) {
(alert) in
}
alert.addAction(add)
alert.addAction(cancel)
present(alert, animated: true, completion: nil)
}
Now update your UITableViewDataSource method as new list item.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! PointsCell
cell.player = players[indexPath.row]
return cell
}
now need to update saveData method to save new list into UserDefaults, you'll call this method whenever you want to save your list.
func saveData() {
UserDefaults.standard.set(try! PropertyListEncoder().encode(self.players), forKey: "items")
}
PointsCell class also need to be updated as new object type:
class PointsCell: UITableViewCell {
#IBOutlet weak var scoreUILabel: UILabel!
#IBOutlet weak var nameUILabel: UILabel!
var player: Player? {
didSet{
if let name = player?.name {
self.nameUILabel?.text = name
}
if let score = player?.score {
self.scoreUILabel?.text = String(score)
}
}
}
#IBAction func pointbuttonPressed(_ sender: Any) {
if self.player != nil{
let score = self.player?.score ?? 0
self.player?.score = score + 1
scoreUILabel.text = String(self.player?.score ?? 0)
}
}
}

Value of type 'Item' has no member 'parentCategory'

I have 2 entities
I'm using XCode 10 right now, I am not sure if I did something wrong or XCode bug.
I added 1 line into these lines
let newItem = Item(context: self.context)
newItem.title = textField.text!
newItem.done = false
newItem.parentCategory = self.selectedCategory <--------- ADD HERE
self.itemArray.append(newItem)
self.saveItems()
Any hints for me on why this is happening ?
TodoListVC
//
// TodoListVC
// ListHue
// Copyright © 2018 LR Web Design. All rights reserved.
//
import UIKit
import CoreData
class TodoListVC: UITableViewController {
var itemArray = [Item]()
var selectedCategory : Category? {
didSet {
loadItems()
}
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// ---------------------------------------------------------------------------------------------------------
//MARK - viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Datasource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "listItemCell", for: indexPath)
let item = itemArray[indexPath.row]
cell.textLabel?.text = item.title
cell.accessoryType = item.done == true ? .checkmark : .none
return cell
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Delegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
context.delete(itemArray[indexPath.row])
itemArray.remove(at: indexPath.row)
itemArray[indexPath.row].done = !itemArray[indexPath.row].done
self.saveItems()
tableView.deselectRow(at: indexPath, animated: true)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Add new item
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Add New Item", message: "", preferredStyle: .alert)
//action
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
let newItem = Item(context: self.context)
newItem.title = textField.text!
newItem.done = false
newItem.parentCategory = self.selectedCategory
self.itemArray.append(newItem)
self.saveItems()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Model Manipulation Methods
func saveItems() {
do {
try context.save()
} catch {
print("Error saving context, \(error)")
}
self.tableView.reloadData()
}
func loadItems(with request: NSFetchRequest<Item> = Item.fetchRequest()) {
let predicate = NSPredicate(format: "parentCategory.name MATCHES %#", selectedCategory?.name!)
request.predicate = predicate
do {
itemArray = try context.fetch(request)
} catch {
print("Error fetching data from the context, \(error)")
}
self.tableView.reloadData()
}
}
//MARK: - Search bar methods
extension TodoListVC : UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let request : NSFetchRequest<Item> = Item.fetchRequest()
request.predicate = NSPredicate(format: "title CONTAINS[cd] %#", searchBar.text!)
request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
loadItems(with: request)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text?.count == 0 {
loadItems()
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
}
}
}
CategoryVC
//
// CategoryVC.swift
// ListHue
// Copyright © 2018 LR Web Design. All rights reserved.
//
import UIKit
import CoreData
class CategoryVC: UITableViewController {
var categories = [Category]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
loadCategories()
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Datasource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath)
let category = categories[indexPath.row]
cell.textLabel?.text = category.name
return cell
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Table View Delegate Methods
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "goToItems", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! TodoListVC
//get the category of the selected cell
if let indexPath = tableView.indexPathForSelectedRow {
//set the property
destinationVC.selectedCategory = categories[indexPath.row]
}
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Data Manipulation Methods
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
print("click")
var textField = UITextField()
let alert = UIAlertController(title: "Add New Category", message: "", preferredStyle: .alert)
//action
let action = UIAlertAction(title: "Add Category", style: .default) { (action) in
let newCategory = Category(context: self.context)
newCategory.name = textField.text!
self.categories.append(newCategory)
self.saveCategories()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
// ---------------------------------------------------------------------------------------------------------
//MARK - Add New Categories
func saveCategories() {
do {
try context.save()
} catch {
print("Error saving context, \(error)")
}
self.tableView.reloadData()
}
func loadCategories(with request: NSFetchRequest<Category> = Category.fetchRequest()) {
do {
categories = try context.fetch(request)
} catch {
print("Error fetching data from the context, \(error)")
}
self.tableView.reloadData()
}
}
I got the diagram backward, thanks to #Larme, and #Ladislav helped me to know that.

Doesnt save userdefaults once the app closes

I have created an app that is a uitableview that has 5 arrays of cell [1, 2, 3, 4, 5] that can edit when the cell is tapped. I have accomplish that when you tapped a cell it shows an alertview and a textfield. You can edit the cell in short. And i have save the edit when it goes to another view controller. But the problem is when i try to close the app and open it, It goes back to the default number of messages like [1, 2, 3, 4, 5]. Any tips how can i save the edited cell but at the same time when the uitableview is opened it shows the array. Sorry for my english. Thanks btw i am new to programming sorry for this code
*Messages.Swift = tableview
class Messages: UITableViewController {
#IBOutlet var uitableView: UITableView!
var tField: UITextField!
var index: Int?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
//Get the array
if let array = UserDefaults.standard.object(forKey:"messages") as? Array<String> {
self.app.helper.messages = array
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return app.helper.messages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let messagesCell = app.helper.messages[indexPath.row]
cell.textLabel?.text = messagesCell
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = tableView.indexPathForSelectedRow
index = path?.row
changeMessage()
}
func changeMessage() {
let alert = UIAlertController(title: "Message", message: "Enter new preset message", preferredStyle: UIAlertControllerStyle.alert)
alert.addTextField(configurationHandler: configurationTextField)
let doneAction = UIAlertAction(title: "Save", style: UIAlertActionStyle.default, handler:{ (UIAlertAction) in
let modelString = self.tField.text
self.app.helper.messages[self.index!] = modelString!
self.app.helper.saveToDefaults()
self.tableView.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(doneAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
func configurationTextField(textField: UITextField!){
if (textField) != nil {
tField = textField
textField.text = app.helper.messages[index!]
}
}
}
*Helper.swift
class Helper {
var messages: [String] = ["1", "2", "3", "4", "5"]
func saveToDefaults() {
let defaults = UserDefaults.standard
defaults.set(messages, forKey: "messages")
defaults.synchronize()
}
}
try
override func viewWillAppear(_ animated: Bool) {
//Get the array
if let array = UserDefaults.standard.object(forKey:"messages") as? Array<String> {
self.app.helper.messages = array
}
//make this array global(declare it below var index: Int?)
tableView.reloadData()
}
and
let messagesCell = array[indexPath.row]
Try to load user default before setting the table view delegate. Because the table view is loading with the dataset you assigned in helper, not with the dataset from user default.
override func viewDidLoad() {
super.viewDidLoad()
//Get the array
if let array = UserDefaults.standard.object(forKey:"messages") as? Array<String> {
self.app.helper.messages = array
}
tableView.delegate = self
tableView.dataSource = self
}
Or you can reload the table view in view will appear after loading the dataset from user defaut
override func viewWillAppear(_ animated: Bool) {
//Get the array
if let array = UserDefaults.standard.object(forKey:"messages") as? Array<String> {
self.app.helper.messages = array
}
tableView.reloadData()
}

Resources