iosswiftuitableviewuicontainerview

Refreshing Child Controllers with Segmented Control


I have parent View Controller containing two Container Views and a segmented control that switches between the two views. Each Container View displays a tableView. The two child view controllers are called printingView and completedView.

A user can mark an item as complete and it is removed from the printingView tableView and displayed on the completedView tableView. The issue I have is refreshing the completedView tableView when the segmentedControl switches over to display that view. As of now, when item is removed from printingView, I need to navigate to another page and come back to PrintJobs ViewController in order to see the item appear in completedView.

As implemented, the two views are hidden and shown appropriately. Since the views are still active on the page, the child tableView does not update when the segmented controller is switched. How can I implement the child tableviews so that it reloads when the segmentedControl is invoked?

Parent VC

class PrintJobsVC: UIViewController {

    @IBOutlet weak var printingView: UIView!
    @IBOutlet weak var completedView: UIView!

    var printer:PrinterDisplay? // passed from the VC before via segue

    @IBAction func toggleSegmentedController(_ sender: UISegmentedControl) {
        switch sender.selectedSegmentIndex {
        case 0:
            // PrintingView
            setView(view: printingView, hidden: false)
            setView(view: completedView, hidden: true)

        case 1:
            // CompletedView
            setView(view: printingView, hidden: true)
            setView(view: completedView, hidden: false)

        default:
            print("hit default case of toggleSegmentedController")
            setView(view: printingView, hidden: false)
            setView(view: completedView, hidden: true)
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "PrintingSegue", let nextVC = segue.destination as? IndividualPrintingVC {
            print("individualPrinting segue")
            nextVC.printer = printer
        } else if segue.identifier == "CompletedSegue", let nextVC = segue.destination as? IndividualCompletedVC {
            print("completedSegue")
            nextVC.printer = printer
        }
    }

    func setView(view: UIView, hidden: Bool) {
        UIView.transition(with: view, duration: 0.3, options: .transitionCrossDissolve, animations: {
            view.isHidden = hidden
        })
    }

}

Child PrintingVC (CompletedVC has the same layout)

class PrintingVC: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    var printer:PrinterDisplay?
    var printingArray:[PrintingDisplay] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
        getCoreData() // retrieves from core data and stores in printingArray
    }
    
    override func viewWillAppear(_ animated: Bool) {
        DispatchQueue.main.async { self.tableView.reloadData() }
    }
}

// tableview methods
extension PrintingVC: UITableViewDelegate, UITableViewDataSource {
    // set up how many rows are in the tableview
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return printingArray.count
    }
    
    // sets up a cell in the tableview
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PrintingCell", for: indexPath as IndexPath) as! PrintingTableViewCell
        
        let item = printingArray[indexPath.row]
        // setup cell parameters

        return cell
    }
}

// mark the item as complete, remove from printingArray, modify Core Data
extension PrintingVC: PrintingTableViewCellDelegate {
    func markCompleted(row: Int, indexPath: IndexPath) {
        let item = printingArray[row]
        // confirm that the user wants to complete print
        let alert = UIAlertController(title: "Are you sure?", message: "\(item.item) will be marked as completed. It can be found in the printer's individual page", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: {action in
            self.markCompletedCoreData(uid: item.uid)
            self.printingArray.remove(at: row)
            self.tableView.deleteRows(at: [indexPath], with: .fade)
        }))
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        self.present(alert, animated: true)
    }
}

Solution

  • There are various ways to do this...


    With your current approach, for each of your child VCs, you are loading your data from Core Data in viewDidLoad() into an array. You then use that array to populate the tables.

    When you mark an item "Completed" in PrintingVC, you update Core Data, remove the element from the array, and remove the row from the table.

    The problem is, that doesn't update the array in CompletedVC.

    So, you would need to have PrintingVC tell PrintJobsVC that a record was changed (using either a closure or protocol / delegate pattern), and then PrintJobsVC would tell CompletedVC to update its array and reload its table.

    That approach is rather "brute force" but would work.


    A better approach could be to use a Fetched Results Controller and Managed Object Context to observe the change to Core Data and automatically update / refresh the table view.

    You'll want to do some research (lots of tutorials out there) to learn how to do that.