0

I have two view controllers to present modal, one on top of another, both are embedded in navigation view controller

In ChatListViewController: Level 1

- (IBAction)ui_mass_message:(id)sender {

    UIViewController *viewController = (UIViewController *)[CocoaHelper viewControllerWithIdentifier:VC_MASS_MESSAGE];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [self presentViewController:navController animated:YES completion:nil];

}

In MassMessageViewController: Level 2

    SelectContactViewController *viewController = (SelectContactViewController *)[CocoaHelper viewControllerWithIdentifier:VC_CONTACT_SELECT];
    [viewController setupWithType:SelectContactTypesMassMessage preSelectedContacts:self.contacts];
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [self presentViewController:navController animated:YES completion:nil];

In SelectContactViewController: Level 3 When I dismiss, I need to pass a parameter to Level 2 (MassMessageViewController), and go 1 level down:

    //TODO: controller is nil
    //I have tried self.navigationController.presentingViewController as well

    MassMessageViewController *controller = (MassMessageViewController *)self.presentingViewController;
    [controller refreshContacts:self.selectedContacts];

    [self.navigationController dismissViewControllerAnimated:YES completion:nil];
OMGPOP
  • 1,995
  • 8
  • 52
  • 95
  • This answer maybe help you http://stackoverflow.com/questions/6606355/pass-value-to-parent-controller-when-dismiss-the-controller – Nguyen Tran Feb 09 '14 at 18:13
  • @NguyenTran no it's not working – OMGPOP Feb 16 '14 at 05:10
  • This is what the delegate pattern is for: http://stackoverflow.com/q/626898/1445366 – Aaron Brager Feb 16 '14 at 05:14
  • the delegate is a bit less human readable when passing the parameter since passing and receiving parts are located in separated chunks of codes. i only use it when i don't have other choices (e.g. core data and networking) – OMGPOP Feb 16 '14 at 05:19
  • plus, delegate is more for broadcasted messages when 'multiple' 'unknown' view controllers are listening, while in this case only one view controller is the recipient. i don't want my code to grow too large – OMGPOP Feb 16 '14 at 05:21
  • Delegation isn't for broadcasting. It's for communication between classes through a protocol so they don't need to know about each other. Your current approach is creating a dependency between your data model and your view hierarchy - if you stick another view controller in between, everything will need to be rewritten. You should have a separate object for managing data; the view controllers should talk to this object instead of to each other. – Aaron Brager Feb 16 '14 at 18:26
  • @AaronBrager oic, i think i confused delegation and observation/notification patterns. – OMGPOP Feb 17 '14 at 06:28

2 Answers2

1

please try this code in last method

UINavigationController *presentingNav = self.presentingViewController;
MassMessageViewController *controller = (MassMessageViewController *)presentingNav.viewControllers[0];
[controller refreshContacts:self.selectedContacts];

[self.navigationController dismissViewControllerAnimated:YES completion:nil];
sage444
  • 5,661
  • 4
  • 33
  • 60
0

The normal way is to use a delegate or call back when you want to pass data back. It's sensible to make the presenting view controller responsible for dismissing the presented viewController.

I would most likely try and get something that looked like this:

- (IBAction)ui_mass_message:(id)sender
{
  UIViewController *viewController = (UIViewController *)[CocoaHelper viewControllerWithIdentifier:VC_MASS_MESSAGE];

  __weak __typeof(self) weakSelf = self;
  viewController.onCompletion = ^(UIViewController *viewController){
    [weakSelf dismissViewControllerAnimated:YES completion:nil];
  };

  UINavigationController *navController  = [[UINavigationController alloc] initWithRootViewController:viewController];
  [self presentViewController:navController animated:YES completion:nil];
}

This will handle dimissing the viewController when the next viewController has "completed". Then you do a similar thing for the next viewController.

SelectContactViewController *viewController = (SelectContactViewController *)[CocoaHelper viewControllerWithIdentifier:VC_CONTACT_SELECT];
[viewController setupWithType:SelectContactTypesMassMessage preSelectedContacts:self.contacts];

__weak __typeof(self) weakSelf = self;
viewController.onCompletion = ^(UIViewController *viewController, NSArray *selectedContacts){
  weakSelf.contacts = selectedContacts;
  [weakSelf dismissViewControllerAnimated:NO completion:nil];
  if (weakSelf.onCompletion) {
    weakSelf.onCompletion(weakSelf);
  }
};

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
[self presentViewController:navController animated:YES completion:nil];

This will again call back to say the viewController has finished but in this case you can also pass back the selectedContacts.

Following simple patterns can really simplify your code base. You could do this with blocks like I have shown or delegates they will both give the same result.

Paul.s
  • 38,494
  • 5
  • 70
  • 88