4

This is a really confusing problem so I will try to explain it as best as I can.

I have view controllers A, B, C, D and E

I'm not sure if I said that right, but to rephrase that, E needs to talk to A, but there's no segue between A and E to set the delegate.

A > B > C > D > E > A

I was told that when you use delegates in swift to access data between view controllers, you have to have a number of things in place:

  1. Define a delegate protocol for object B.
  2. Give object B an optional delegate variable. This variable should be weak.
  3. Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
  4. Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
  5. Tell object B that object A is now its delegate.

I have been using prepapreforsegue to pass an object between view controllers as they navigate from one to the next, adding to it as they continue through the prepareforsegue method.

On the final view controller, I have protocol methods that are meant to be executed the first view controller, but because the last view controller segues from a different view controller. Problem is it's supposed to execute those methods on the first view controller. How can it reach it without that first view controller being able to tell the last view controller that it's the segue.

Here is some simple code to show you my problem and what I mean.

First:

import UIKit

class FirstViewController: UITableViewController, LastViewControllerDelegate {

var entry: EntryItem!

override func viewDidLoad() {
    super.viewDidLoad()
    entry = EntryItem()
    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

...

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    //here's where I access the next view controller and set controller.entry and controller.delegate
    let controller = segue.destinationViewController as NextViewController
    controller.entry = entry
}

func lastViewControllerDidCancel(controller: LastViewController, didNotFinishAddingEntry draftentry: EntryItem){
    //this is where I wanna do something
    dismissViewControllerAnimated(true, completion: nil)
}
func lastViewController(controller: LastViewController, didFinishAddingEntry finalentry: EntryItem){
    //this is where I wanna do something
    dismissViewControllerAnimated(true, completion: nil)
}
}

Next 1:

import UIKit


class Next1ViewController: UITableViewController, LastViewControllerDelegate {

var entry: EntryItem!

override func viewDidLoad() {
    super.viewDidLoad()

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

...

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    //here's where I access the next view controller and set controller.entry and controller.delegate
    let controller = segue.destinationViewController as Next2ViewController
    controller.entry = entry
}


}

Next 2:

import UIKit


class Next2ViewController: UITableViewController, LastViewControllerDelegate {

    var entry: EntryItem!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()
    }

    ...

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        //here's where I access the next view controller and set controller.entry and controller.delegate
        let controller = segue.destinationViewController as LastViewController
        controller.entry = entry
        controller.delegate = ???? //Not self, but needs to be the first one.
    }


}

Last:

import UIKit

protocol LastViewControllerDelegate: class {
    func lastViewControllerDidCancel(controller: LastViewController, didNotFinishAddingEntry draftentry: EntryItem)
    func lastViewController(controller: LastViewController, didFinishAddingEntry finalentry: EntryItem)
}

class LastViewController: UITableViewController {

weak var delegate: LastViewControllerDelegate?
var entry: EntryItem!

override func viewDidLoad() {
    super.viewDidLoad()

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

...


@IBAction func cancel(){
    delegate?.lastViewControllerDidCancel(self, didNotFinishAddingEntry: entry)
}

@IBAction func done(){
    //do some final additions to entry object
    delegate?.lastViewController(self, didFinishAddingEntry: entry)
}

}
Nathan McKaskle
  • 2,926
  • 12
  • 55
  • 93
  • 1
    Could you amend the `prepareForSegue` code in `NextViewController` to set `controller.delegate = self.delegate`? Because self.delegate refers to the FirstViewController, the methods should then be called on the FirstViewController. – pbasdf Dec 27 '14 at 20:45
  • @pbasdf I think I typed my answer as you were typing this comment! We're probably definitely right then. – jrturton Dec 27 '14 at 20:54
  • @jrturton I think it will work, but with 5 VCs your suggestion of using notifications is probably the better course. – pbasdf Dec 27 '14 at 20:57
  • Really? Wait look at the post again, I edited it to be way less confusing, hopefully. The only delegate that needs to be in place is between the first and last, not between the first and second or second and third. Not sure if that matters. – Nathan McKaskle Dec 27 '14 at 21:02
  • Added a section to my answer explaining. If the middle vc's have no use for the delegate, use notifications. – jrturton Dec 27 '14 at 21:09

1 Answers1

2

Can't you make your first view controller conform to all the delegate protocols (or, probably, make a single delegate protocol, otherwise you'd get casting problems) and then instead of doing this:

controller.delegate = self

Just do

controller.delegate = self.delegate

Alternatively, if you've got things several steps removed, using notifications is often better than delegation. If the intervening view controllers have no purpose for the delegate other than to forward it on, you should use notifications.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • Yeah this can't work, I tried it. That would only work if there were actually only 3 view controllers, that was just an example, but I'll fix it in the post because that's confusing, sorry. There's far more. There's a first, second, third, fourth, fifth, last. – Nathan McKaskle Dec 27 '14 at 21:10
  • In that case, definitely use notifications. – jrturton Dec 27 '14 at 21:14
  • 1
    I will have to look up notifications, I'm not sure what those are in this context, but thanks, I'll let ya know if that worked. – Nathan McKaskle Dec 27 '14 at 21:15
  • Yeah I couldn't find anything on notifications, I have no idea what you mean. The only context in which I have heard about notifications is with push notifications. – Nathan McKaskle Dec 27 '14 at 22:10
  • 1
    @Sephethus I had this issue today as well... Found this SO question and the last answer helped a lot with NSNotificationCenter... http://stackoverflow.com/questions/24049020/nsnotificationcenter-addobserver-in-swift – Byron Coetsee Dec 27 '14 at 22:12
  • Yes, NSNotificationCenter is what I'm talking about. – jrturton Dec 27 '14 at 22:23
  • Ah ok, thanks! I'll try to figure out how to apply that to this and somehow pass the completed object back to the first view controller. – Nathan McKaskle Dec 28 '14 at 00:31