Pass data through navigation back button

Luca Alberto picture Luca Alberto · Jan 22, 2016 · Viewed 18.8k times · Source

I am in this situation:

img1

I am passing 4 array from Progress Table to Detail Exercise using prepare for segue and it works fine! The problem begin when I try to pass the data back from the Detail Exercise Controller to the Progress Table Controller. I would like to use the default navigation back button to go back to the parent view. Actually I'm using this code but it doesn't work, well the data pass from the child to the parent view but i can't see the result in the Progress Table, seems like i need to refresh but i also tryed the reloadData after the viewDidLoad and it doesn't work. Any suggestion? Thank you.

override func viewWillDisappear(animated : Bool) {
    super.viewWillDisappear(animated)

    if (self.isMovingFromParentViewController()){
        print("n'drio")

        let historyView = self.storyboard!.instantiateViewControllerWithIdentifier("historyView") as! HistoryTableViewController
        historyView.isFirstTime = false
        historyView.arrayData = arrayDataDetails
        historyView.arrayRipetizioni = arrayRipetizioniDetails
        historyView.arrayPeso = arrayPesoDetails
        historyView.arrayRecupero = arrayRecuperoDetails
        historyView.tableView.reloadData()
    }
}

Answer

Eendje picture Eendje · Jan 22, 2016

When you press the back button, the navigation controller will call navigationController(willShowViewController:) so you can use this to pass the data back to your initial view controller. An example is shown below:

Using UINavigationControllerDelegate:

class DetailsViewController: UIViewController, UINavigationControllerDelegate {
                                                        //     ^
    var data: [String] = []                             // Important!

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationController?.delegate = self

        data = ["data has changed!"]
    }
}

Swift 2:

extension DetailsViewController: UINavigationControllerDelegate {
    func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
        if let controller = viewController as? ProgressTableViewController {
            controller.data = data    // Here you pass the data back to your original view controller
        }
    }
}

Swift 3:

extension DetailsViewController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        (viewController as? ProgressTableViewController)?.data = data // Here you pass the to your original view controller
    }
}

In this example, the key is using the UINavigationControllerDelegate and setting the delegate of the navigation controller (in this case it's self). Having done this, you can send the data back to your initial view controller with the back button.

Personally I prefer using a class for my data:

Using a custom class for your data:

class Data {
    var array: [String] = []
}

Progress view controller:

class ProgressTableViewController: UITableViewController {

    var data = Data()

    override func viewDidLoad() {
        super.viewDidLoad()

        data.array = ["some data"]
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        tableView.reloadData()
    }

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

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()

        cell.textLabel?.text = data.array[indexPath.row]

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "exerciseSegue", sender: self)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "exerciseSegue" {
            let destination = segue.destinationViewController as! DetailsViewController
            destination.data = data
        }
    }
}

Details view controller:

class DetailsViewController: UIViewController {

    var data = Data()

    override func viewDidLoad() {
        super.viewDidLoad()

        data.array = ["data has changed!"]
    }
}

In the last example, you don't have to worry about passing around data. Whenever you change the data, the controllers using the same class, will have the changes as well.