I'm trying to do a simple reusable class which will fire up an action sheet to chose pic from library or camera and then crop the image and just return the final image to caller.
But when I tap on any button in UIActionSheet, its getting crashed showing EXC_BAD_ACCESS
.
Here is the code:
In my MainViewController.m
- (IBAction)ClickOnAdd:(id)sender{
ImagePickerControl *pickerControl = [[ImagePickerControl alloc] initWithController:self Title:@"Chose Image" delegate:self cancelButtonTitle:@"Cancel" fromLibraryTitle:@"Library" fromCameraTitle:@"Camera"];
[pickerControl showInView:self.view];
}
Code in ImagePickerControl.h
@protocol ImagePickerControlDelegate;
@interface ImagePickerControl : NSObject<UIActionSheetDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (retain, nonatomic) UIActionSheet *menuSheet;
@property (assign, nonatomic) UIViewController *parentViewController;
- (id)initWithController:(UIViewController*) viewController Title:(NSString *)title delegate:(id<ImagePickerControlDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle fromLibraryTitle:(NSString *)fromLibTitle fromCameraTitle:(NSString *)fromCamTitle;
-(void)showInView:(UIView*) parentView;
@end
@protocol ImagePickerControlDelegate <NSObject>
// Called when an image is picked. The view will be automatically dismissed after this call returns
- (void)imagePickerControlDidPickImage:(ImagePickerControl *)imagePickerControl pickedImage:(UIImage*)selectedImage;
// Called when we cancel the control's view
- (void)imagePickerControlDidCancel:(ImagePickerControl *)imagePickerControl;
@end
And finally in ImagePickerControl.m
- (id)initWithController:(UIViewController*) viewController Title:(NSString *)title delegate:(id<NTImagePickerControlDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle fromLibraryTitle:(NSString *)fromLibTitle fromCameraTitle:(NSString *)fromCamTitle
{
if(self = [super init]){
self.menuSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self cancelButtonTitle:cancelButtonTitle
destructiveButtonTitle:nil
otherButtonTitles:fromLibTitle,fromCamTitle,nil];
self.parentViewController = viewController;
}
return self;
}
-(void)showInView:(UIView*) parentView
{
[self.menuSheet showInView:parentView];
}
//This method was not called at all
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
}
After a quick research, from this SO post, I think, its clear that my control class gets released after i invoke. But in the answer there, it is mentioned to assign the controller as a strong property to mainviewcontroller. But I dont want to use this by creating a property and assigning. Just simply call it on add button click and get the callbacks, just like we use UIActionSheet or UIAlertView.
One more note is that, in my ClickOnAdd
method, if I remove my custom class and add UIActionSheet itself there, then the callbacks are working fine. I'm expecting the same behaviour using my class. How that can be done?
Thanks in advance.
As already mentioned in the other answer, your ImagePickerControl
is deallocated before the button is tapped. Therefore your app crashes.
You should assign the picker controller to a strong ivar in ClickOnAdd
method. Then in your callback (I assume MainViewController
gets a callback when the button is tapped) you should set this ivar to nil
so that the picker controller is released.
It could work without an ivar
if you weren't using ARC
. In that case, you can keep the picker controller in the memory by not releasing it on ClickOnAdd
and manually releasing it on the callback. However, that is just an invitation to memory leaks, therefore I would use a retained property even with non-ARC
code.
UIActionSheet
is a subclass of UIView
therefore I assume it is added to the view hierarchy when you call showInView
and therefore retained. It is probably released as soon as it is removed from the hierarchy so it is also good to keep a strong reference to it just in case it gets released sooner than you expected.