In my iOS app, a user can select an image from a list, upon which they are presented with a modal that contains the image and options to delete the image. If the user chooses to delete the image, she is returned to the original viewController containing the list of images. I need to then refresh the original ViewController to take into account the deleted image.
I tried using NSNotificationCenter to broadcast when an image is deleted to the parent View Controller. However, it seems like the broadcast is never received.
Is there some other way to
- send data back to the parent ViewController after the modal is dismissed, and
- detect when the modal is dismissed from the parent ViewController?
(I tried following the example outlined here, but it didn't seem to work)
Below is my code:
EditStepViewController (original View Controller):
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
MediaPreviewViewController *mediaPreviewVC = (MediaPreviewViewController *)[storyboard instantiateViewControllerWithIdentifier:@"MediaPreviewViewController"];
mediaPreviewVC.selectedImageURL = [NSString stringWithFormat:@"%@",gestureRecognizer.view.tag];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:mediaPreviewVC];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didDismissMediaPreview)
name:@"MediaPreviewDismissed"
object:nil];
[self presentViewController:navigationController animated:YES completion:nil];
MediaPreviewViewController (second ViewController):
...
[self deleteImage];
[[NSNotificationCenter defaultCenter] postNotificationName:@"MediaPreviewDismissed" object:nil userInfo:nil];
[self dismissViewControllerAnimated:YES completion:^(){
NSLog(@"dismissed controller");
}];
Then, back in EditStepViewController:
-(void)didDismissMediaPreview{
NSLog(@"dismissed media preview"); // this is never logged!
[self.view setNeedsDisplay]; // refresh view to account for deleted image
}
Thanks in advance for your help!
In my case I usually use block here.
For example you have ParentViewController.h
@interface ParentViewController : UIViewController
@end
Implementation ParentViewController.m
// INCLUDE HERE THE MODAL CONTROLLER TO HAVE ACCESS TO ITS PUBLIC PROPERTY
#import ModalViewController.h
@implementation ParentViewController
// implement your modal dismiss block here
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// DEFINE HERE THE CALLBACK FUNCTION
// 1. get the model view controller
ModalViewController *mvc = [segue destinationViewController];
// 2. Your code after the modal view dismisses
mvc.onDismiss = ^(UIViewController *sender, NSObject *objectFromModalViewController)
{
// Do your stuff after dismissing the modal view controller
.
.
.
}
}
@end
And, ModalViewController.h
@interface ModalViewController : UIViewController
// call back function, a block
@property (nonatomic, strong) void (^onDismiss)(UIViewController *sender, NSObject *objectYouWantToPassBackToParentController)
@end
ModalViewController.m
@implementation ModalViewController
.
.
.
// your dismiss function say
- (IBAction)dismissViewController:(id)sender
{
...
[self deleteImage];
[self dismissViewControllerAnimated:YES completion:^
{
// MAKE THIS CALL
self.onDismiss(self, theOjectYouWantToPassBackToParentVC);
}];
}
@end
You should make a protocol in your MediaPreviewViewController. Then when the image is deleted, send a delegate method so the parent viewcontroller can handle that.
You also should never let the viewcontroller dismiss itself (though it is possible, but recommended that the view which created the modal is also responsible for removing the modal...)
So you should get something like this:
In MediaPreviewViewController.h:
@protocol MediaPreviewViewControllerDelegate <NSObject>
-(void)didRemovedImage;
@end
@interface MediaPreviewViewController : NSObject {
id< MediaPreviewViewControllerDelegate > delegate;
}
@property (nonatomic, assign) id < MediaPreviewViewControllerDelegate> delegate;
In MediaPreviewViewController.m:
@synthesize delegate = _delegate;
Then in your method in your MediaPreviewViewController where you remove the image, you just call:
[_delegate didRemoveImage];
In your parent viewcontroller, you need to implement this protocol like you are used to with delegates.. You then can also remove the view from the parent in this delegate method
This may help anyone who have same problem on UIPopovePresentationController. From my experience, Allan's answer can resolve the same issue too.
I've just have problem about Viewcontroller's delegate that present as UIPopoverPresentingController didn't send call to its root view. And Allan's answer can solved this fit.
My sample code:
-(IBAction)choosePic:(id)sender
{
UIButton *picButton = (UIButton *)sender;
// Set PopoverPresentation view controller
PicViewController *picVC = [self.storyboard instantiateViewControllerWithIdentifier:@"PicViewController"];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:picVC];
picVC.preferredContentSize = CGSizeMake(500., 500.);
// Set this function as described in Allan's answer
picVC.dismissPopover = ^(UIViewController *controller, UIImage *image)
{
_myImage = image;
.....
.....
.....
};
navController.modalPresentationStyle = UIModalPresentationPopover;
_picPopover = navController.popoverPresentationController;
_picPopover.delegate = self;
_picPopover.sourceView = self.view;
_picPopover.sourceRect = [picButton frame];
navController.modalPresentationStyle = UIModalPresentationPopover;
navController.navigationBarHidden = YES;
[self presentViewController:navController animated:YES completion:nil];
}