Search code examples
iosswiftuitableviewpersistenceuistoryboardsegue

how to get data to save to specific category in tableview instead on all categories?


I have a groceryList app when you add an item to the category list it adds to the entire list of categories when is should not!

https://github.com/mrbryankmiller/Grocery-TableView-.git

class GroceryItemsTableViewController: UITableViewController {

    //var groceryItem = ["Item1", "Item2", "Item3"]

    //var groceryList  = ["Breakfast","Lunch", "Dinner"]


    @IBOutlet var groceryItemTableView: UITableView!


    @IBAction func addGroceryItemButtonPressed(sender: UIBarButtonItem) {

        ///new way///

        let alertController: UIAlertController = UIAlertController(title: "Add Grocery Item", message: "", preferredStyle: .Alert)

        //Cancel Button

        let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
            //cancel code
        }
        alertController.addAction(cancelAction)
        let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { action -> Void in

            let textField = alertController.textFields![0]
            groceryItem.items.append(textField.text!)
            self.tableView.reloadData()

        }

        alertController.addAction(saveAction)


        //Add text field

        //        alertController.addTextFieldWithConfigurationHandler { (textField) -> Void in
        //        textField.textColor = UIColor.blackColor()

        alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
            textField.placeholder = "Enter an Item"
            //alertController.textFields
        }

        //Present the AlertController
        self.presentViewController(alertController, animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        //self.navigationItem.leftBarButtonItem = self.editButtonItem()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows

        return groceryItem.items.count

    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCellWithIdentifier("groceryItem1", forIndexPath: indexPath)
        cell.textLabel!.text = groceryItem.items [indexPath.row]
        return cell

    }
}

Solution

  • If you see carefully the declaration of your class groceryItem you have a static array of elements for every item in your grocery list so every time you add a new element it's shared among all the grocery items.

    Instead you should have for each grocery item a list associated with each of its items.

    You could define a new struct to save for each grocery item its list of item associated like in the following way:

    struct GroceryItem {
       var name: String
       var items: [String]
    }
    

    The we are going to change a little the code in your GroceryListTableViewController to refactor the code according your new model, so it should be like the following:

    GroceryListTableViewController:

    class GroceryListTableViewController: UITableViewController, GroceryItemsTableViewControllerProtocol {
    
    
        var groceryList = [GroceryItem]()
    
        @IBAction func addButton(sender: UIBarButtonItem) {
    
           let alertController: UIAlertController = UIAlertController(title: "Add Grocery Category", message: "", preferredStyle: .Alert)
    
           //Cancel Button
    
           let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
            //cancel code
           }
           alertController.addAction(cancelAction)
    
           let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { action -> Void in
    
              let textField = alertController.textFields![0]
              self.groceryList.append(GroceryItem(name: textField.text!, items: [String]()))
              self.tableView.reloadData()
           }
           alertController.addAction(saveAction)
    
           alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
              textField.placeholder = "Enter an Item"
              //alertController.textFields
            }
    
           //Present the AlertController
           self.presentViewController(alertController, animated: true, completion: nil)
        }
    
        override func viewDidLoad() {
    
           super.viewDidLoad()
    
           //edit button
           self.navigationItem.leftBarButtonItem = self.editButtonItem()
    
           groceryList.append(GroceryItem(name: "Breakfast", items: ["Item1", "Item2",  "Item3"]))
           groceryList.append(GroceryItem(name: "Lunch", items: ["Item1", "Item2",  "Item3"]))
           groceryList.append(GroceryItem(name: "Dinner", items: ["Item1", "Item2",  "Item3"]))
    
        }
    
        override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
           return groceryList.count
        }
    
        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
           let cell = tableView.dequeueReusableCellWithIdentifier("prototype1", forIndexPath: indexPath) as UITableViewCell
    
           cell.textLabel!.text = groceryList [indexPath.row].name
    
           return cell
        }
    
       // pass a tableview cell value to navigationBar title in swift//
    
       override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
          let destinationVC = segue.destinationViewController as! GroceryItemsTableViewController
          let cell = sender as! UITableViewCell
    
          let idx = self.tableView.indexPathForSelectedRow?.row
    
          destinationVC.delegate = self
          destinationVC.itemList = groceryList[idx!].items
          destinationVC.navigationItem.title = cell.textLabel?.text
       }
    
       func didAddGroceryItem(itemName: String) {
          let idx = self.tableView.indexPathForSelectedRow?.row
          groceryList[idx!].items.append(itemName)
       }
    
       func didRemoveGroceryItem(index: Int) {
        let idx = self.tableView.indexPathForSelectedRow?.row
        groceryList[idx!].items.removeAtIndex(index)
       }  
    }
    

    In the above I have refactored all the code regarding the new model, I put only the places where the code change the rest keep the same.

    The thing you need to pass the item associated with the cell selected to the another UIViewController and you can do it very easily in your prepareForSegue. For that we need to get the index for the selected cell and pass the elements to the another UIViewController where we have a new array of [String] created as data source to show the items.

    The another important point in the code is that the GroceryListTableViewController now implements a new protocol called GroceryItemsTableViewControllerProtocol. This protocol it's the way to notify to GroceryListTableViewController from the GroceryItemsTableViewController every time a new item is added to the list it's called the delegate pattern.

    GroceryItemsTableViewController:

    protocol GroceryItemsTableViewControllerProtocol: class {
    
       func didAddGroceryItem(itemName: String)
    
       func didRemoveGroceryItem(index: Int)
    }
    
    class GroceryItemsTableViewController: UITableViewController {
    
         weak var delegate: GroceryItemsTableViewControllerProtocol?
    
         var itemList: [String]!
    
         @IBAction func addGroceryItemButtonPressed(sender: UIBarButtonItem) {
    
            ///new way///
    
           let alertController: UIAlertController = UIAlertController(title: "Add Grocery Item", message: "", preferredStyle: .Alert)
    
           //Cancel Button
    
           let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
              //cancel code
           }
           alertController.addAction(cancelAction)
    
    
           let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { [weak self] action -> Void in
    
              guard let s = self else { return }
    
              let textField = alertController.textFields![0]
    
              s.itemList.append(textField.text!)
              s.delegate?.didAddGroceryItem(textField.text!)
              s.tableView.reloadData()
    
           }
    
          alertController.addAction(saveAction)
    
          alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
             textField.placeholder = "Enter an Item"
            //alertController.textFields
          }
    
          //Present the AlertController
          self.presentViewController(alertController, animated: true, completion: nil)
      }
    
      override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return itemList.count
       }
    
    
      override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
          let cell = tableView.dequeueReusableCellWithIdentifier("groceryItem1", forIndexPath: indexPath)
    
    
          cell.textLabel!.text = itemList[indexPath.row]
          return cell
    
      }
    
      override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            // Delete the row from the data source
            itemList.removeAtIndex(indexPath.row)
            delegate?.didRemoveGroceryItem(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }
      }
    }
    

    EDIT: To handle properly the deletion you should create a new delegate method no notify the GroceryListTableViewController that a item has been deleted and then delete it properly and you can see in the updated code above.

    I hope this help you.