2

I did find an answer to this title and I did do a little research but I'm still not getting the flow. Here is what I want to happen:

1) click a button on the presenter view to open a modal view. 2) retrieve some value and click a button to close the modal view....sending the value to the presentor view and execute a method.

I get that this works like a callback but I still can't figure out where to put the callback stuff.

So, how exactly do I do this? A) In the presentViewController completion block, should I include the presenter view method to execute when modal view is completed?

Or: B) In the modal view's dismissViewControllerAnimated completion block, should I include the presenter view method to execute when modal view is completed?

Can somebody help me with some sample code? Or at least help me get the flow of which block to put the code in?

Thank you, P

Patricia
  • 5,019
  • 14
  • 72
  • 152

3 Answers3

9

You talk about completion blocks so I am assuming you do not want to use delegates.

In the viewController that will be presented modally you need to provide a public completion handler, that will be called when it is dismissed.

@interface PresentedViewController : UIViewController

@property (nonatomic, strong) void (^onCompletion)(id result);

@end

Then in the implementation you need to call this completion block on dismissal. Here I assume the viewController is dismissed on a button click

- (IBAction)done:(id)sender
{
  if (self.onCompletion) {
    self.onCompletion(self.someRetrievedValue);
  }
}

Now back in the viewController that presented the modal you need to provide the actual completion block - normally when you create the viewController

- (IBAction)showModal;
{
  PresentedViewController *controller = [[PresentedViewController alloc] init];
  controller.onCompletion = ^(id result) {
    [self doSomethingWithTheResult:result]
    [self dismissViewControllerAnimated:YES completion:nil];
  }
  [self presentViewController:controller animated:YES completion:nil];
}

This will create the new viewController to be presented modally and define what needs to happen on completion.

Paul.s
  • 38,494
  • 5
  • 70
  • 88
  • Nice solution! But shouldn't block properties always have the "copy" attribute? Or is that not necessary anymore with iOS 6? – Martin R Nov 30 '12 at 21:43
  • Under ARC I believe you can just use `strong` check the [docs](http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.blocks) (the wording is quite confusing so I could be wrong but I'm sure I've seen it mentioned before as well). But pre-ARC (who's doing that anymore?) you would indeed need to use copy. – Paul.s Nov 30 '12 at 21:58
  • Even with ARC you have to copy a block to move the block from the stack to the heap (see e.g. http://stackoverflow.com/questions/10417851/under-arc-are-blocks-automatically-copied-when-assigned-to-an-ivar-via-the-prop, there might be better references). – Martin R Dec 01 '12 at 09:24
  • I'm not sure that clear it up any :S. Just for kicks I added a breakpoint with `break set -n _Block_copy` in llvm and it did indeed hit the break point on assignment to the `strong` ivar – Paul.s Dec 01 '12 at 11:52
  • Did you test it with iOS 6? The behavior might have changed. I am quite sure that a `strong` assignment did not copy a block before, but I do not have a definite reference at present. I can try to find one if you are really interested. - But I do not insist on discussing this, it was just something that came into my mind when I read your answer. – Martin R Dec 01 '12 at 12:01
  • I've tried it on iOS 5 and 6 sims and get the same result. I think it's the same logic as returning a block from a method - ARC just does the right thing and copies before returning.I have to admit I wasn't sure and couldn't find any references either apart from the confusing llvm docs – Paul.s Dec 01 '12 at 12:03
  • OK, thank you for the information. If I find something then I will add it here, otherwise we can leave it like this. – Martin R Dec 01 '12 at 12:07
2

You can do this with delegates, that's the way Apple seems to recommend, but that seems like overkill to me. You have a reference to the presenter with the presentingViewController property, so you can just set the value of a property in the presenter from the presented controller in the button click method:

self.presentingViewController.someProp = self.theValueToPass;
[self dismissViewControllerAnimated:YES];
rdelmar
  • 103,982
  • 12
  • 207
  • 218
1

Using delegates is a good way to handle this:

In your PresentedViewController.h

@protocol PresentedViewControllerDelegate <NSObject>

-(void) viewWillDismiss;

@end

@property (nonatomic, weak) id <PresentedViewController> delegate;

Then in your PresentingViewController.h, you would subscribe to this delegate

@interface PresentingViewController : UIViewController <PresentedViewControllerDelegate>

in the .m you must implement the delegate method

- (void) viewWillDismiss {

}

and before you present the view controller set the delegate property you made as self.

presentingViewController.delegate = self;

Obviously not every implementation detail has been done here, but this should get you started.

Adam Johnson
  • 2,198
  • 1
  • 17
  • 23