1
votes

Let's say we have two view controllers, a parent with a label and a modally presented child with a table view. How would I pass the user's selection in the table view back to the parent using delegation?

ViewController1:

   var delegate: vc2delegate?

   override func viewDidLoad {
        super.viewDidLoad()
        let label.text = ""
   }

ViewController2:

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
            let selections = ["1", "2", "3", "4", "5"]
            cell.selections.text = selections[indexPath.row]
            return cell
       }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) as? Cell {
            cell.didSelect(indexPath: indexPath as NSIndexPath)

            }

        dismiss(animated: true, completion: nil)
        } 
   //wherever end of class is

   protocol vc2delegate {
      // delegate functions here
   }

Do I even have the right approach? I never really got down this pattern and I think it's crucial for me to learn for iOS. Another tricky caveat may be that viewDidLoad() doesn't get called when you dismiss a modal view controller.

2

2 Answers

3
votes

Take a look at the UIViewController life cycle docs: ViewDidLoad only gets called once.

There are plenty of guides on how to do this, just do a quick search. You'll need to update the dataSource logic as I added a quick string array, and you'll most likely have something a bit more complex, but the idea is still the same.

BTW, I used your naming convention of vc1/vc2, but I hope you have more meaningful names for your controllers.

In your code you have the delegate on the wrong VC. Here is a quick code sample of what it should look like:

class VC1: UIViewController {

    let textLabel = UILabel()

    // whenever you're presenting the vc2
    func presentVC2() {
        var vc2 = VC2()
        vc2.delegate = self
        self.present(vc2, animated: true, completion: nil)
    }
}

extension VC1: VC2Delegate {
    func updateLabel(withText text: String) {
        self.textLabel.text = text
    }
}


protocol VC2Delegate: class {
    func updateLabel(withText text: String)
}

class VC2: UIViewController {
    weak var delegate: VC2Delegate?
    let dataSource = ["string 1", "tring 2"]
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let string = dataSource[indexPath.row]
        self.delegate?.updateLabel(withText: string)
        dismiss(animated: true, completion: nil)
    }
}
0
votes

You can use callback function also to update your label from tableview:

1) Declare callback function into your VC2:

var callback:((String) -> Void)?

2) Call this function in your tableview CellForRowAt method in VC2:

let dataSource = ["string 1", "tring 2"]

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let string = dataSource[indexPath.row]  
      var cell = tableView.dequeueReusableCell(withIdentifier: "yourCell") as! YourCell

     //here you can call callback function & pass string to VC1
     cell.callback?(dataSource[indexPath.row])
}

3) Now you can call callback closure in VC1 in anywhere you call your VC2:

class VC1: UIViewController {

    let textLabel = UILabel()

    //I'm calling this(presentVC2()) function on ViewDidLoad you can call anywhere you want
    func viewDidLoad() {
        super.viewDidLoad()
        presentVC2()
    }

    // whenever you're presenting the vc2
    func presentVC2() {
        var vc2 = VC2()
        vc2.callback = { text in
            self.textLabel.text = text
        }
        self.present(vc2, animated: true, completion: nil)
    }
}