6

The relevant part of my storyboard appears as follows: http://imgur.com/09livAb You can see the custom "Container Controller" view houses two Container Views, one which links to a Navigation Controller via embedded segue, and another that links to a custom "Master View Controller" (which implements a Table View Controller) via embedded segue. The Navigation Controller component further has a relationship with a custom "Location Filter Controller."

I need to implement delegation such that when one of the UISteppers in the Location Filter Controller is incr./decr., the table view in the Master View Controller knows to update the data it displays accordingly.

I am not unaccustomed to working with protocols/delegates, but this unique situation of talking between views housed in segues is really tricking me! For the most part I have had success following the example here: Passing Data between View Controllers. In this case however, I am not able to directly link instantiated views as he indicates to do in 'Passing Data Back' step 6.

I had considered using a singleton object from which each of these views could get/set the necessary data, but the issue here is that the table view would not necessarily know when to update its contents, despite having data with which it could/should update.

Here is a code snippet from ContainerController.m where I setup the embedded segues to function:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    DataHold *data = [[DataHold alloc] init]; // <-- this actually is a singleton object

    if([segue.identifier isEqualToString:@"locationEmbedSegue"])
    {

    }
    else if([segue.identifier isEqualToString:@"tableEmbedSegue"])
    {
        [[segue destinationViewController] setDelegate:data.detailTableViewController];
        // ^ This part actually sets up a delegate so that the table view (Master View Controller)
        // delegates to the detail view controller of the overarching split view controller
        // and tells it what to display when a row is pressed.
    }
}

Thanks for any help!

Community
  • 1
  • 1
OneManBand
  • 528
  • 5
  • 24
  • You should embed your storyboard screenshot directly in your question, for ease of reading and future reference (better not rely on third-party hosting services). – Guillaume Algis Jun 05 '13 at 21:40
  • Haha, yeah. I'd love to do that, but I am still rep-poor and I need a total of 15 rep currency to embed screenshots! – OneManBand Jun 05 '13 at 22:17
  • Hu. Forgot about that. Sorry :D – Guillaume Algis Jun 05 '13 at 22:45
  • No problem, lol. Once I get 15 rep I'll probably come back and replace the few external img links with embedded pictures on the questions I've asked so far. – OneManBand Jun 06 '13 at 17:32
  • Have you implemented the delegate protocol using GuillaumeA's answer? It still seems to me that the delegate assignment is backwards, but maybe I'm misunderstanding what you want. Since the changes are being made by the stepper in LocationFilterController, it needs to be the one calling the delegate method, and the table view controller (not the table view) needs to be the one implementing the delegate method -- that makes the table view controller the delegate of the detail controller, not the other way around. – rdelmar Jun 06 '13 at 19:36

2 Answers2

4

I think you are on the right track setting the table view delegate to your Location Filter Controller.

I found that a simple way to work with embeded view controller is to add "placeholders" property for them, and set these property when the segue is "performed".

// MyContainerController.h
@property (strong, nonatomic) MyLocationFilterController *detailViewController;
@property (strong, nonatomic) UITableViewController *masterViewController;

// MyContainerController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([segue.identifier isEqualToString:@"locationEmbedSegue"])
    {
        UINavigationViewController *dest = (UINavigationViewController *)segue.destinationViewController;
        self.detailViewController = dest.topViewController;
    }
    else if([segue.identifier isEqualToString:@"tableEmbedSegue"])
    {
        self.masterViewController = (UITableViewController *)segue.destinationViewController;
        [self.masterViewController.tableView setDelegate:self.detailViewController];
    }
}
Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
  • 1
    There's a potential problem with this approach. If the "tableEmbedSegue" is called first, then self.detailViewController will be null. I found through experimentation that the segue called first is the one connected to the container view listed first in the main view's subviews (in the scene list on the left hand side of the screen in IB). So, make sure that the container view connected to "locationEmbedSegue" is the top one -- if not, just drag it up. Also I think the last line has the delegate assignment backwards -- should be self.detailViewController.delegate = self.masterViewController – rdelmar Jun 05 '13 at 23:10
  • I don't think there is an error on the delegate assignment, I'll just edit the code to set the delegate of the `UITableView` intead of the `UITableViewController` but other than that it seems right. – Guillaume Algis Jun 06 '13 at 09:10
  • Hey! Worked like a charm. Great idea to set a property and then just assign the views to them so that I can hold on to their references throughout the app. Thank you very much! – OneManBand Jun 06 '13 at 15:00
0

I came to this question recently and found there may be one problem with the answer above.

  • Move the setDelete: method out. This makes sure no controller is nil.

Then code becomes:

// MyContainerController.h
@property (strong, nonatomic) MyLocationFilterController *detailViewController;
@property (strong, nonatomic) UITableViewController *masterViewController;

// MyContainerController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([segue.identifier isEqualToString:@"locationEmbedSegue"])
    {
        UINavigationViewController *dest = (UINavigationViewController *)segue.destinationViewController;
        self.detailViewController = dest.topViewController;
    } else if([segue.identifier isEqualToString:@"tableEmbedSegue"])
    {
        self.masterViewController = (UITableViewController *)segue.destinationViewController;

    }

    [self.masterViewController.tableView setDelegate:self.detailViewController];
}
Kevin
  • 1
  • 2
  • why not just do self.masterViewController.tableView.delegate = self; there is no requirement for the delegate to to be the same controller as the datasource. – malhal Jan 28 '17 at 19:46