Search code examples
iosobjective-cmodalviewcontrollerpresentmodalviewcontroller

Updating a presenting view controller's property inside the modal view controller


I was wondering how I would be able to update a view controller property of the presenting view controller from a method called inside the modal view controller? Right now, the only way I could think of is using NSNotificationCenter, and while this works for Dictionary items, I couldn't figure out how to use it for a custom object.

For example, my presenting view controller HomewViewController has a Parse PFObject called homeSelections, which the could be updated in the modally presented ModalViewController's property newSelections (and also a PFObject) . After the user makes her selections, I would like HomeViewController's homeSelections to also have the latest data passed from the modal view controller.

Help is appreciated - thanks.

Update 1: here is what I have done now (note that I am using a stripped down example to test things out)

In ViewController (this is the parent/presenting view controller)

@interface ViewController ()

@property (strong, nonatomic) NSArray *totalRamen;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.totalRamen = @[@"ramen1", @"ramen2", @"ramen3", @"moot"];

}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    NSLog(@"self.totalRamen: %@", self.totalRamen);
    NSLog(@"Done");
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"showModal"]){
        ModalViewController *destinationVC = (ModalViewController *)segue.destinationViewController;
        destinationVC.passedRamen = self.totalRamen;
    }
}

- (IBAction)showModalAction:(UIButton *)sender
{
    ModalViewController *destinaionViewController = [[ModalViewController alloc] init];
    destinaionViewController.selectionCallback = ^(id selectedItem) {
        self.totalRamen = (NSArray *)selectedItem;
        NSLog(@"self.totalRAmen %@", self.totalRamen);
        NSLog(@"done");
    };
    [self performSegueWithIdentifier:@"showModal" sender:self];
}

In ModalViewController (this is the presented/modal view controller)

@interface ModalViewController : UIViewController

@property (strong, nonatomic) NSArray *passedRamen;
- (IBAction)dismissModal:(UIButton *)sender;

typedef void(^CallbackBlock)(id value);
@property (nonatomic, copy) CallbackBlock selectionCallback;

@end

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    NSLog(@"Passed ramen %@", self.passedRamen);
    NSLog(@"Done");

    self.passedRamen = @[@"moot is awesome"];
    NSLog(@"new ramen: %@", self.passedRamen);
    NSLog(@"%@", self.selectionCallback); //nil here

    //call back
    CallbackBlock selectionCallback = self.selectionCallback;
    if (selectionCallback){
        selectionCallback(self.passedRamen); //I want to send the newly updated self.passedRamen back
    } else {
        NSLog(@"No show"); //Means if isn't called
    }
}

- (IBAction)dismissModal:(UIButton *)sender
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

Solution

  • Pass a callback block into the presented view controller. This way the presented view controller doesn't know anything about the view controller presenting it. Much more flexible because now anybody can present your view controller, they just pass it a block!

    PresentingViewController

    PresentedViewController *vc = [[PresentedViewController alloc] init]; //or get your existing one
    vc.selectionCallback = ^(id selectedItem) {
        //update selected items here
    };
    //present vc here
    

    PresentedViewController

    typedef void(^CallbackBlock)(id value);
    
    @property (nonatomic, copy) CallbackBlock selectionCallback;
    
    - (void)somethingWasSelected:(id)selectedItem {
        CallbackBlock selectionCallback = self.selectionCallback;
        if (selectionCallback) selectionCallback(selectedItem);
    }
    

    Beware of retain cycles. This block is being retained by the presented view controller so references to the presented view controller in the block without weakifying it first will create a leak. More info on this can be found here.