1

I have a tableview in a view, vc1, when an item is selected I want to go to vc2 modally where the selected item can be edited. When I dismiss vc2 programatically via a "back" button, how can I notify vc1 so that it can reload() the tableView and show the edited data?

I can pick up the vc2 swipe dismissal in vc1 by:

dismiss(animated flag: Bool, completion: (() -> Void)?)

and, for my app, this is the equivalent of "cancel", i.e. no action taken. However, I want to identify in vc1 when the modal is closed programatically. I think I need to use a completion handler but I'm struggling to understand what is needed.

Jim Burke
  • 251
  • 3
  • 14
  • Does this answer your question? [Detect when a presented view controller is dismissed](https://stackoverflow.com/questions/32853212/detect-when-a-presented-view-controller-is-dismissed) – Petar Jul 15 '23 at 08:52
  • I think it comes close to it, especially the solution provided by Rory McKinnel, but I have spent hours trying to understand it and get it to work and can't. I don't follow the block logic. – Jim Burke Jul 15 '23 at 09:10

1 Answers1

0

Below is some example code showing one way to pass data from the presented view controller (VC2) to the presenting view controller (VC1).

VC2 defines var callback: (() -> Void)?, which is a function type. It defines a function that has no parameters and returns nothing. Prepare(for segue:) in VC1 provides the callback function to VC2 as a block of code. The callback updates VC1's data and reloads VC1's table view.

In VC2, when Done is selected, it calls the callback function before being dismissed.

class VC1: UIViewController, UITableViewDataSource {
    
    var data = [1, 2, 3, 4, 5, 6, 7]

    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
    }
    
    // MARK: - TableViewDataSource

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = "\(data[indexPath.row])"
        return cell
    }

    // MARK: - Navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "Show Number" {
            if let cell = sender as? UITableViewCell {
                let indexPath = tableView.indexPath(for: cell)
                if let vc2 = segue.destination as? VC2 {
                    vc2.number = data[indexPath!.row]
                    vc2.callback = {
                        self.data[indexPath!.row] = vc2.number
                        self.tableView.reloadData()
                    }
                }
            }
        }
    }
}
class VC2: UIViewController {

    var number = 0
    var callback: (() -> Void)?
    
    @IBOutlet weak var numberLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        numberLabel.text = "\(number)"
    }
    
    @IBAction func add100Selected(_ sender: UIButton) {
        number += 100
        numberLabel.text = "\(number)"
    }
    
    @IBAction func doneSelected(_ sender: UIButton) {
        callback?()
        dismiss(animated: true)
    }
}

Full Screen Modal Segway

Using a Full Screen modal segue, you are forced to use the Done button, which always saves the changes.

Full Screen Modal Segway

Page Sheet Modal Segway (swipeable)

Using a Page Sheet modal segue, the changes are not saved if you swipe VC2 away. If you want the changes to be saved no matter how you dismiss VC2, you would have to call the callback function right after you modify the data (at the end of add100Selected, in my example).

Page Sheet Modal Segway

I used the same VC code for each video (just changed the segue presentation in Interface Builder).

Interface Builder

Here's how I set up the two VC's in Interface Builder. I selected the segue in the image, so you can see its settings in the Attributes inspector.

Interface Builder

P. Stern
  • 279
  • 1
  • 5
  • Thanks, the answer is very much appreciated. I note however that VC2 is not presented modally (maybe I am wrong, but modals don't use a segue). Would I be correct to assume that that what I am looking to achieve can't be done with a modally presented VC2? – Jim Burke Jul 16 '23 at 19:27
  • I set it up as a Modal segue in Interface Builder, by control-dragging from the table view cell to VC2, and selecting "Present Modally". I then set the Segue Presentation as Full Screen (in the Attributes inspector). After re-reading your question, I guess you used the default Segue Presentation (Page Sheet), which allows you to swipe it away. I updated my answer to show a page sheet modal segue. – P. Stern Jul 17 '23 at 01:04
  • Brilliant, thanks so much for explaining it, I understand the process now. Works like a dream! – Jim Burke Jul 17 '23 at 08:54