0

I have a table view controller that displays names of shopping lists. These shopping lists are created through a shopping list class, which lets me specify the name of the shopping list and items in it. This table view displays around 5 shopping lists (names). When I press one, I go to another table view controller, which shows the items of the shopping list. However, when I go back to the shopping list (names) table view controller and press another shopping list, it still displays the items from the old shopping list and not the one that was pressed. I think I have narrowed the problem down to the cellForItemAt of the second table view controller not being called again.

import UIKit

class List: NSObject {

    var name: String
    var items: [String?]
    var shared: String?

    init(name: String, items: [String], shared: String?) {
        self.name = name
        self.items = items
        self.shared = shared
    }

}

enum ListType {
    case apply
    case view
}

class ListViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId"

    var lists: [List] = [
        List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
        List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
        List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
        List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
        List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
    ]

    //Create Class Lazy Vars
    lazy var newListVC: NewListViewController = {
        let launcher = NewListViewController()
        //        launcher.homeController = self
        return launcher
    }()

    lazy var listItemsVC: ListItemsViewController = {
        let launcher = ListItemsViewController()
        //        launcher.homeController = self
        return launcher
    }()

    //Setup Enums and Switches
    var listType: ListType!


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)

        //SWITCH STATEMENT
        switch listType as ListType {

        case ListType.apply:
            //Apply Begin
            navigationItem.title = "Apply List"

            //Setup Navigation Bar Items
            let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)

            //Add items to navigation bar
            navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
            //Apply End
        case ListType.view:
            //View Begin
            //Setup Navigation Bar
            navigationItem.title = "My Lists"

            //Add items to navigation bar
            navigationItem.rightBarButtonItem = addBarButtonItem
            navigationItem.leftBarButtonItem = editButtonItem

            //View End
        }
        //SWITCH STATEMENT END


        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

    }

    @objc func newList() {
        newListVC.pageControlFunc(pageView: PageView.name)
        navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return lists.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        listItemsVC.listName = lists[indexPath.row].name
        listItemsVC.listItems = lists[indexPath.row].items
        print(listItemsVC.listItems)
        navigationController?.pushViewController(listItemsVC, animated: true)
    }

}


///////////////////////////////////////////////////////////////////////////

SECOND UITABLEVIEW - NOT UPDATING

///////////////////////////////////////////////////////////////////////////

import UIKit

class ListItemsViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId1"

    var listName: String?
    var listItems: [String?] = []


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        //Set Title
        navigationItem.title = listName

        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

        //Delegate method not called on push to view

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return listItems.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path

        return cell
    }

}

Thanks in advance!

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • Please show your current code – Kayla Galway Jan 23 '18 at 22:48
  • Please refer to this solution. https://stackoverflow.com/questions/28430663/send-data-from-tableview-to-detailview-swift – ahmed Jan 23 '18 at 23:15
  • @ahmed Thanks for the suggestion. Sorry, I think I was unclear in my question. I can pass data between viewControllers, but I think it is that when I push a viewController for the second time, it does not update the cellForItemAt: indexPath, even though the variable that is affecting it is being changed. – Nikolas Ioannou Jan 24 '18 at 01:36
  • @KaylaGalway Sorry I am new, how do I do this? – Nikolas Ioannou Jan 24 '18 at 01:36
  • Click edit on your question and then copy and paste the current table view code - any code that may or may not be involved in your question from your two tableviewcontrollers – Kayla Galway Jan 24 '18 at 01:37
  • @KaylaGalway, Done. So, I select a list and it works, but then when I go back and select another list, it is still displaying the items of the old list. – Nikolas Ioannou Jan 24 '18 at 01:45
  • Is there a reason you are using lazy vars for those two view controllers? It doesn't look like you have any particular reason to be doing that? – Kayla Galway Jan 24 '18 at 01:50

1 Answers1

1

ViewDidLoadis called once, when the ViewController is first loaded into memory. Since your ListsItemsViewController is a strong property on your ListViewController, and you are navigating to it multiple times, nothing is ever telling it to update its views or reload your tableview because it does not get deallocated(so ViewDidLoad and the TableView delegate/datasource methods do not get called). You also haven't added any custom code telling it to update.

If you want to keep those view controllers as class properties of ListViewController, then you can add tableView.reloadData in the ViewWillAppear method of ListsItemsViewController as shown below.

import UIKit

class List: NSObject {

    var name: String
    var items: [String?]
    var shared: String?

    init(name: String, items: [String], shared: String?) {
        self.name = name
        self.items = items
        self.shared = shared
    }

}

enum ListType {
    case apply
    case view
}

class ListViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId"

    var lists: [List] = [
        List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
        List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
        List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
        List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
        List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
    ]

    //Create Class Lazy Vars
    lazy var newListVC: NewListViewController = {
        let launcher = NewListViewController()
        //        launcher.homeController = self
        return launcher
    }()

    lazy var listItemsVC: ListItemsViewController = {
        let launcher = ListItemsViewController()
        //        launcher.homeController = self
        return launcher
    }()

    //Setup Enums and Switches
    var listType: ListType!


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)

        //SWITCH STATEMENT
        switch listType as ListType {

        case ListType.apply:
            //Apply Begin
            navigationItem.title = "Apply List"

            //Setup Navigation Bar Items
            let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)

            //Add items to navigation bar
            navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
            //Apply End
        case ListType.view:
            //View Begin
            //Setup Navigation Bar
            navigationItem.title = "My Lists"

            //Add items to navigation bar
            navigationItem.rightBarButtonItem = addBarButtonItem
            navigationItem.leftBarButtonItem = editButtonItem

            //View End
        }
        //SWITCH STATEMENT END


        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

    }

    @objc func newList() {
        newListVC.pageControlFunc(pageView: PageView.name)
        navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return lists.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        listItemsVC.listName = lists[indexPath.row].name
        listItemsVC.listItems = lists[indexPath.row].items
        print(listItemsVC.listItems)
        navigationController?.pushViewController(listItemsVC, animated: true)
    }

}


///////////////////////////////////////////////////////////////////////////

SECOND UITABLEVIEW - NOT UPDATING

///////////////////////////////////////////////////////////////////////////

import UIKit

class ListItemsViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId1"

    var listName: String?
    var listItems: [String?] = []


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        //Set Title
        navigationItem.title = listName

        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

        //Delegate method not called on push to view

    }

    //This will always get called when this view controller's view is added to the window
    //so you can guarantee it will call your delegate methods to reload the tableView with your new data
    override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(animated: animated)
      tableView.reloadData()
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return listItems.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path

        return cell
    }

}

The other option would be removing those lazy vars and just initializing those view controllers as needed, so that they get deallocated once you remove them from the stack. This would mean that your tableviews would always reload without having to add any code in ViewWillAppear

import UIKit

class List: NSObject {

    var name: String
    var items: [String?]
    var shared: String?

    init(name: String, items: [String], shared: String?) {
        self.name = name
        self.items = items
        self.shared = shared
    }

}

enum ListType {
    case apply
    case view
}

class ListViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId"

    var lists: [List] = [
        List(name: "Veggies", items: ["Fruit Loops", "Pasta"], shared: nil),
        List(name: "11/17/18", items: ["Eggs", "Green Beans", "Pirate Booty", "Bread", "Milk"], shared: nil),
        List(name: "Fruits", items: ["Fruit Loops", "Oranges"], shared: nil),
        List(name: "Red Foods", items: ["Apples", "Tomatoes", "Watermelon", "Cherries"], shared: nil),
        List(name: "Grains", items: ["Bread Crumbs", "Pasta", "Rice", "Chicken Flavored Rice"], shared: nil)
    ]

    //Setup Enums and Switches
    var listType: ListType!


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        let addBarButtonItem = UIBarButtonItem(title: "+", style: .plain, target: self, action: #selector(newList)) //+ Button
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .normal)
        addBarButtonItem.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Light", size: 27)!], for: .selected)

        //SWITCH STATEMENT
        switch listType as ListType {

        case ListType.apply:
            //Apply Begin
            navigationItem.title = "Apply List"

            //Setup Navigation Bar Items
            let locateBarButtonItem = UIBarButtonItem(title: "Locate Item", style: .plain, target: self, action: nil)

            //Add items to navigation bar
            navigationItem.rightBarButtonItems = [addBarButtonItem, locateBarButtonItem]
            //Apply End
        case ListType.view:
            //View Begin
            //Setup Navigation Bar
            navigationItem.title = "My Lists"

            //Add items to navigation bar
            navigationItem.rightBarButtonItem = addBarButtonItem
            navigationItem.leftBarButtonItem = editButtonItem

            //View End
        }
        //SWITCH STATEMENT END


        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

    }

    @objc func newList() {
        let newListVC = NewListViewController()
        newListVC.pageControlFunc(pageView: PageView.name)
        navigationController?.pushViewController(newListVC, animated: true) //Then pushes listVC
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return lists.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = self.lists[indexPath.row].name //Then sets cell of indexPath text = lists value of index path

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let listItemsVC = ListItemsViewController()
        listItemsVC.listName = lists[indexPath.row].name
        listItemsVC.listItems = lists[indexPath.row].items
        print(listItemsVC.listItems)
        navigationController?.pushViewController(listItemsVC, animated: true)
    }

}


///////////////////////////////////////////////////////////////////////////

SECOND UITABLEVIEW - NOT UPDATING

///////////////////////////////////////////////////////////////////////////

import UIKit

class ListItemsViewController: UITableViewController {

    //Initialize Variables Here
    let cellId = "cellId1"

    var listName: String?
    var listItems: [String?] = []


    override func viewDidLoad() {
        super.viewDidLoad()

        //Things that both cases have in common:
        navigationController?.navigationBar.prefersLargeTitles = true

        //Set Title
        navigationItem.title = listName

        //Register TableView with Id: cellId
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)

        //Delegate method not called on push to view

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return listItems.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        cell.textLabel?.text = listItems[indexPath.row] //Then sets cell of indexPath text = lists value of index path

        return cell
    }

}
Kayla Galway
  • 662
  • 4
  • 7