The imagePickerController hates me, eats my photos
I have a magic trick app that's been on the store since 2008 but still doesn't work reliably, and I feel horrible about it. The app takes a picture as part of the trick, but if device memory is low / fragmented / or the stars are aligned improperly, it often just sort of drops the picture. The unfortunate user is halfway through a magic trick when, after taking his pic, he sees not the image he just shot, but the application's home screen. I have tried to debug this myself on and off but I'm just not equal to the task (I'm definitely in the "hobbyist" category, like my nick implies).
My current, unreleased version of the app uses a simple .png file as a camera overlay to make performing part of the trick easier, but it didn't make the dreaded lost photo bug any better or worse. I have long assumed that resizing the raw photo to just 360x480 (that's as close as you can get to screen resolution without changing the aspect ratio or cropping the photo down) would reduce my RAM needs and eliminate the bug, but I've got that implemented in the below code and it didn't make the problem better or worse either.
Any obvious thoughts from looking at these methods guys?
Code:
- (IBAction)takePhoto {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
// code for using overlay and an NSTimer to get rid of it from http://stackoverflow.com/questions/2081872/camera-overlay-view-just-for-preview
UIImagePickerController *anImagePickerController = [UIImagePickerController new];
anImagePickerController.delegate = self;
anImagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
outline.hidden = YES;
anImagePickerController.cameraOverlayView = outline;
[self presentModalViewController:anImagePickerController animated:NO];
[anImagePickerController release];
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(timerFireMethod:)
userInfo:outline
repeats:YES];
}
}
// this method is also taken from the stackoverflow discussion web
- (void)timerFireMethod:(NSTimer*)theTimer {
UIView *cameraOverlayView = (UIView *)theTimer.userInfo;
UIView *previewView = cameraOverlayView.superview.superview;
if (previewView != nil) {
[cameraOverlayView removeFromSuperview];
[previewView insertSubview:cameraOverlayView atIndex:1];
cameraOverlayView.hidden = NO;
[theTimer invalidate];
}
}
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo {
[self useImage:image];
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
}
- (void)useImage:(UIImage *)theImage { // resize raw iPhone photo to screen res to try and save some memory
CGSize screenSize = {360,480};
UIGraphicsBeginImageContext( screenSize );// a CGSize that has the size you want
[theImage drawInRect:CGRectMake(0,0,screenSize.width,screenSize.height)];
//image is the original UIImage
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[theImage release];
userPhoto.image = newImage; // userPhoto is UIImageView that displays the photo after the camera interface is dismissed
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[cardView release];
[super dealloc];
}
I'm not sure if this is what you need without seeing more code.... but you aren't retaining the image anywhere... try this..
but also post your your viewDidLoad, and viewDidUnload methods for the viewController class that these methods are a part of. It might be that the picker is burning too much memory, so your hidden view (the view the picker is hiding) might be unloading unexpectedly (while its hidden by the picker), and when it reloads right before it is shown once the picker view is dismissed, it might be doing some property initialization that you aren't expecting... I would put a few NSLog statements in your viewDidLoad and unload methods to see if they are getting fired when you aren't expect it.
Can I have my Hundred Dollars now
Code:
- (void)useImage:(UIImage *)theImage { // resize raw iPhone photo to screen res to try and save some memory
UIImage *myRawImage = [theImage retain];
CGSize screenSize = {360,480};
UIGraphicsBeginImageContext( screenSize );// a CGSize that has the size you want
[myRawImage drawInRect:CGRectMake(0,0,screenSize.width,screenSize.height)];
//image is the original UIImage
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[myRawImage release];
userPhoto.image = newImage; // userPhoto is UIImageView that displays the photo after the camera interface is dismissed
}
Last edited by BSDimwit; 02-26-2010 at 01:28 AM.
Reason: forgot the explanation.
You aren't retaining the image sent to the delegate method. That image is an autorelease object and when you dismiss the view, it gets dropped from the view stack..., it releases and poof, your image is gone... by retaining it, you give your delegate method time to actually do the image resize before the image is ripped out from under it by the autorelease. Of course, because you retain it, you must release it when you are done using it.
Can I have my Hundred Dollars now
I want to have your babies! That actually makes some kind of sense, even to my reptilian sized brain. As soon as I un-break my code from some unrelated but badly needed UI changes, I'll confirm it's working so that your full glory can be known to all. I see you haven't forgotten about my original thinking of a bounty... lol... I'm happy to oblige, private message me your email address and I'll paypal you after I'm sure the bug is good and truly dead. It's worth that much 2x over because this bug has been driving my poor magician customers crazy, and this is the more profitable of my two apps (though that's not saying much - heh).
reread my post, I saw a few things I might have missed
subject says it all.... And ignore my line about the bounty... I was kidding. This site has helped me a bunch of times for free so its nice to return the favor when I can. I also have recently been playing the same methods... doing almost exactly the same sort of thing in fact so I was just at the right place at the right time... which is why I am telling you to check your viewDidLoad and unload methods. It might be that if you are initing a few image properties in viewDidLoad, you might want to move those to an awakeFromNib method so that your inits aren't redone by a load and unload, then subsequent reload of the view.
That's because this code is much older than iPhone OS 3.0. I got so frustrated over some bugs that I couldn't squash that I went into retirement for a year, feeling I was out of my league trying to write apps for the store. Now I've taken a couple Java classes, and still have no clue--- BUT, when I re-read iPhone programming tutorials I actually understand a fair bit. I think I'm six months from marginal competence at long last! lol
Should I write a viewDidUnload method? This app will rely heavily on the image overlay in the camera, so it's going to be 3.1+ only once the update is released.
Thanks for this help Todd, of course this morally obligates me to come back later when I'm less clueless and help some other poor sap.
The viewDidLoad method is a bit confusing because it displays a false home screen for the trick (complete with nifty calendar that shows the real date). The method displays my fake screen that can do absolutely nothing except use its working "camera" button, that triggers the takePhoto method (the spectator doesn't realize an app is running when the camera icon is pressed). You could easily be right about this just being related to my failure to retain the image, I'll test the code extensively in a couple hours when I get the app back in workable condition... Anyhow, here's viewDidLoad, for completeness but it's probably not helpful:
Maybe best to let me wrestle with implementing your thoughts so far, and I'll report back on where I stand tomorrow. I'll also try sprinkling some NSLog statements around and see if it helps me get a clue.
Yeah, your viewDidLoad doesn't have any of the things I was worried about so see how retaining that UIImage works for you.
I will look over your code and overlay generation methods a bit more to see if I can't find anything else that gives me pause.
Todd
Sadly, the bug still lives, though it seems to take a more badly RAM deprived iPhone to make it misbehave now (but it's hard to be certain). I will be adding NSLog statements this evening and try to understand my own code better before I look to the next steps. 'tis truly a nasty bug.
Sadly, the bug still lives, though it seems to take a more badly RAM deprived iPhone to make it misbehave now (but it's hard to be certain). I will be adding NSLog statements this evening and try to understand my own code better before I look to the next steps. 'tis truly a nasty bug.
If you want to shoot me a copy of your project, I would be happy to look at it further... I've been using an overlay view in one of my apps (camera app) and it doesn't suffer from the same thing as yours. Of course I promise to do nothing nefarious with your code and will keep it in the strictest confidence, but I can certainly understand if you hesitate to do that.
If you want to shoot me a copy of your project, I would be happy to look at it further... I've been using an overlay view in one of my apps (camera app) and it doesn't suffer from the same thing as yours. Of course I promise to do nothing nefarious with your code and will keep it in the strictest confidence, but I can certainly understand if you hesitate to do that.