App Doesn't Behave Correctly On Relaunch - objective-c

So, the app that I'm writing allows the user to upload a photo from their camera roll, and it displays in a UIImageView. I have a "Save" button, that allows the images to be saved when pressed. I also have an "Edit" button, when tapped, it allows the user to tap on the photo and it will be deleted. This is where I'm having issues.
On test runs when I add in three images, hit the save button, then delete them, they delete perfectly. I can completely close and relaunch the app and the images are no longer there. But, if I add in three images, hit the save button then close and relaunch the app, then try to delete the photos, it does not work at all. The images disappear from the screen, but load again on relaunch. Very strange. I'm totally new to Objective-C, I'm surprised that I even made it this far, so I'm having issues figuring out how to delete it from the array.
I have NSLog(#"User Clicked Yes. Deleting index %d of %d", alertView.tag, [array count]); in ym code, and when I delete it before closing the app, it displays that the array count has objects in it. When I run it from Xcode again and try to delete a picture thats already been saved, it indicates that the array count = 0. The objects are not in the array on relaunch. So, the issue is that the objects in the array aren't being saved properly. I have no idea why not though, I thought I was saving them correctly. This is how I add them into the array:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
if (imageView.image == nil) {
imageView.image = img;
[self.array addObject:imageView];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView2.image == nil) {
imageView2.image = img;
NSLog(#"The image is a %#", imageView);
[self.array addObject:imageView2];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
...
...
-(IBAction)saveButtonPressed:(id)sender {
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES) objectAtIndex:0];
NSLog(#"Image view array before saving = %#", self.array);
for (UIImageView *imageViewToSave in self.array) {
NSInteger tag = imageViewToSave.tag;
UIImage *image = imageViewToSave.image;
NSString *imageName = [NSString stringWithFormat:#"Image%i.png",tag];
NSString *imagePath = [docsDir stringByAppendingPathComponent:imageName];
NSLog(#"Saving %# to %#", image, imagePath);
[UIImagePNGRepresentation(image) writeToFile:imagePath atomically:NO];
}
NSLog(#"Save Button Pressed");
}
I thought that this would save the objects that were placed into the array, but apparently not. I have no idea how else to go about doing this.
And just for reference, I threw my .h and .m files in a wiki. Here is my entire .h file:
github.com/Joey502/Test-Thing/wiki/.h-Class-File And here is my entire .m file: github.com/Joey502/Test-Thing/wiki/.m-Class-File

The problem is in your viewDidLoad method, there you have initialised self.array but when loading the images into your UIImageViews you have forgotten to add them to the array, which is why the delete function isn't working. Your delete function is trying to delete an object from an array that is empty.
Inside your if statements you need to add the UIImageView to self.array (Note: you'll need to change self.array to a NSMutableArray for this particular method to work)
if (!imageView.image) {
imageView.image = loadedImage;
[self.array addObject:imageView];
}
else if (!imageView2.image) {
imageView2.image = loadedImage;
[self.array addObject:imageView2];
}
and so on.

Related

Issue with tags in Xcode

I have an app that displays 9 UIImageViews, and I'm adding in the ability for the user to delete them. The code works fine, except I'm having some issues with using tags in interface builder. It seems that my code deletes the image in the ImageView based upon its position in the array. So, if I have 3 UIImageViews, I will set the first UIImageViews tag to 1, the second image view's tag to 2, and the third to 3. I try to delete the third one first, and I get the error:
Terminating app due to uncaught exception NSRangeException, reason: *' -[__NSArrayM removeObjectAtIndex:]: index 3 beyond bounds [0 .. 1]'
This is the code that I'm using:
- (IBAction)deleteButtonPressed:(id)sender {
NSLog(#"Sender is %#", sender);
UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:#"Delete"
message:#"Are you sure you want to delete this photo?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[deleteAlertView show];
int imageIndex = ((UIButton *)sender).tag;
deleteAlertView.tag = imageIndex;
}
- (void)alertView: (UIAlertView *) alertView
clickedButtonAtIndex: (NSInteger) buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex]) {
NSLog(#"User Clicked Yes. Deleting index %d of %d", alertView.tag, [array count]);
NSLog(#"The tag is %i", alertView.tag);
[self.array removeObjectAtIndex: alertView.tag];
NSLog(#"After deleting item, array count = %d", [array count]);
NSLog(#"Returned view is :%#, in view: %#", [self.view viewWithTag:alertView.tag], self.view);
((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil;
}
[self.user setObject:self.array forKey:#"images"];
}
I have to put the first image in the 0 index of the array, but I know that you can't have a tag of 0 since it is the default tag. So, I have no idea how to get around this issue. Any help is much appreciated, thanks!
I don't think static tags as index numbers will work for this because, even if you get it right for the first delete, the match-up of tags and array offsets will change if you delete something that's not the last array element.
You'd be better to write a method to step through the array and return the item with a tag that matches. Or, if you want direct access via a tag, use a dictionary and build the keys using the tags.
Code for tag matching would look something like:
- (UIImageView *)viewForTag:(NSInteger)tag {
UIImageView *found = nil;
for (UIImageView *view in self.array) {
if (tag == view.tag) {
found = view;
break;
}
}
return found;
}
- (void)alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex {
// ...
// Replacing: [self.array removeObjectAtIndex: alertView.tag];
UIImageView *view = [self viewForTag:alertView.tag];
if (view) {
[self.array removeObject:view];
}
// ...
}

iOS: Simple NSMutableArray Call

I need to call removeObject in one of my methods, but I can't figure out how to do this correctly. I'm very new to Objective-C, and am still learning the basics. I have an app that behaves somewhat like a photo gallery, and displays UIImageViews. I'm implementing the option to have the user delete photos from their gallery. To accomplish this, I decided to place an invisible button over each picture. When the user hits an "Edit" button, the hidden delete button over each picture becomes active (I'm using the same IBOutlet over each of the hidden buttons, for simplicity). When the user taps the button over the picture, an alert view appears asking if they really want to delete it. If they click yes, deleteAlertView comes into play:
- (void)deleteAlertView:(UIAlertView *)deleteButtonPressed
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex != [deleteButtonPressed cancelButtonIndex]) {
[array removeObject:#"%#", deleteButtonPressed];
}
The issue here is [array removeObject:#"%#", deleteButtonPressed];, I did the %# so that this will automatically determine which object in the array was tapped, rather than manually putting in a new method and button for each UIImageView (I may have to end up doing that). I'm getting errors regarding "array" and "deleteButtonPressed" (use of undeclared identifier), I can't for the life of me figure out what to put instead. I'm still learning the basics and how inheritance in this language works. Any help or advice would be great! I should probably post the whole view controller file to show the related inheritance:
- (IBAction)grabImage {
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.delegate = self;
self.imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
_popover = [[UIPopoverController alloc] initWithContentViewController:imgPicker];
[_popover presentPopoverFromRect:self.imageView.bounds inView:self.imageView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else {
[self presentModalViewController:imgPicker animated:YES];
}
[self.imgPicker resignFirstResponder];
}
// Sets the image in the UIImageView
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
if (imageView.image == nil) {
imageView.image = img;
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView2.image == nil) {
imageView2.image = img;
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView3.image == nil) {
imageView3.image = img;
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
}
- (void)viewWillAppear:(BOOL)animated
{
self.user = [NSUserDefaults standardUserDefaults];
NSMutableArray* array = [[self.user objectForKey:#"images"]mutableCopy];
while(array == nil)
{
[self.user setObject:[NSMutableArray arrayWithObject:#""] forKey:#"images"];
array = [[self.user objectForKey:#"images"]mutableCopy];
NSLog(#"%#",#"attempting to create an array to store the images in");
}
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
NSMutableArray* array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)]];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)]];
[self.user setObject:array forKey:#"images"];
[user synchronize];
}
- (void)viewDidLoad
{
self.user = [NSUserDefaults standardUserDefaults];
NSLog(#"It is %#", self.user);
NSMutableArray* array = [[self.user objectForKey:#"images"]mutableCopy];
imageView.image = [[UIImage alloc] initWithData:[array objectAtIndex:0]];
imageView2.image = [[UIImage alloc] initWithData:[array objectAtIndex:1]];
imageView3.image = [[UIImage alloc] initWithData:[array objectAtIndex:2]];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
backToGalleryButton.hidden = YES;
tapToDeleteLabel.hidden = YES;
deleteButton1.hidden = YES;
[super viewDidLoad];
}
- (IBAction)deleteButtonPressed:(id)sender {
UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:#"Delete"
message:#"Are you sure you want to delete this photo?"
delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No", nil];
[deleteAlertView show];
}
- (void)deleteAlertView:(UIAlertView *)deleteButtonPressed
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex != [deleteButtonPressed cancelButtonIndex]) {
[array removeObject:#"%#", deleteButtonPressed];
}
}
There is one thing wring here, but first a relatively minor point:
- (void)deleteAlertView:(UIAlertView *)deleteButtonPressed didDismissWithButtonIndex:(NSInteger)buttonIndex
the phrase "deleteButtonPressed" implies an even, as it ends in a verb. It Is actually referring to an object, specifically a parameter of the type UIAlertView. you should call it something more like AlertView.
Secondly this line is quite wrong:
[array removeObject:#"%#", deleteButtonPressed];
You are trying to remove a string. If that method accepted an argument list (where you pass multiple objects separated by a comma), you would be removing literally "deleteButtonPressed". You want to remove the object that is being pointed to by the deleteButtonPressed variable. So all you have to do is:
[array removeObject:deleteButtonPressed];
The issue here is [array removeObject:#"%#", deleteButtonPressed];
Yes, that is one of the issues (even ignoring the invalid syntax). The array does not contain your UIAlertView, it contains whatever objects [user objectForKey:#"images"] contains. Which seem like they should be NSData instances and which in any case are definitely not your UIAlertView instance(s).
So in other words, you can't pass the UIAlertView to the array in order to have the array magically work out what item the UIAlertView is supposed to correspond to. Instead what you should do is tag the UIAlertView with the index it corresponds to when you create it. You can do this like:
UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:#"Delete"
message:#"Are you sure you want to delete this photo?"
delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No", nil];
int imageIndex = <figure out the index of the associated array element based upon 'sender'>;
deleteAlertView.tag = imageIndex;
...and then when the button is pressed, you do:
[array removeObjectAtIndex:deleteButtonPressed.tag];
And to fix up that "undeclared identifier" issue, you should declare array in your header and not in viewDidLoad. You want it to be a private instance variable, not a local variable.
Also note that deleting an element from [[user objectForKey:#"images"] mutableCopy] will not automatically cause the corresponding element to be deleted from [user objectForKey:#"images"]. You need to write the modified array back to [NSUserDefaults standardUserDefaults] if you want the modification to actually persist.
You are getting the error "Use of undeclared identifier array" because you declare the array in different methods, but not in your deleteAlertView method. I suggest reading up on variable scope.
Fixing that, however, will not get your code to work because you have some fundamental design flaws that need to be worked out.
You mention that you are a beginner, so I would suggest reading through and completing several beginner tutorials before attempting this app. I know it is fun to dive right into a project, but you will very likely get frustrated and also develop bad habits with respect to app design/engineering. In particular, I would try to get a firmer understand of variable scope and MVC design patterns.

Xcode Unrecognized Selector Error

I've been having a ton of issues with this section of an app that I'm writing, I'm sure people here are getting sick of me so I'm going to try and solve all my questions in this post. I'm working on an app that behaves like a photo gallery, and I'm implementing the option to have the user delete photos from their gallery. To accomplish this, I decided to place an invisible button over each picture. When the user hits an "Edit" button, the hidden delete buttons over each picture become active. I'm using the same IBOutlet over each of the hidden buttons for simplicity, and I've tagged each button appropriately in Interface Builder. When the user taps the button over the picture, an alert view appears asking if they really want to delete it. If they click yes, I call removeObjectAtIndex and delete the image from the UI. But, when I click "Yes" in the alert view, I get an error from Xcode stating:
2012-04-04 11:26:40.484 AppName[608:f803] -[UIButton setImage:]: unrecognized selector sent to instance 0x6a922c0
2012-04-04 11:26:40.485 AppName[608:f803] Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIButton setImage:]: unrecognized selector sent to instance 0x6a922c0'*
I've been trying to figure out what is causing this for a few hours now to no avail. I'm not setting the image of a UIButton anywhere in my code. I did in IB, but I simply set the buttons types to Custom so that they appear invisible. I will post my entire file below, I can't find any issues in my code, so any help is much appreciated! Thanks.
EDIT Here is the current version of the code:
- (IBAction)grabImage {
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.delegate = self;
self.imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
_popover = [[UIPopoverController alloc] initWithContentViewController:imgPicker];
[_popover presentPopoverFromRect:self.imageView.bounds inView:self.imageView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else {
[self presentModalViewController:imgPicker animated:YES];
}
[self.imgPicker resignFirstResponder];
}
// Sets the image in the UIImageView
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
if (imageView.image == nil) {
imageView.image = img;
self.array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView2.image == nil) {
imageView2.image = img;
NSLog(#"The image is a %#", imageView);
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView3.image == nil) {
imageView3.image = img;
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView4.image == nil) {
imageView4.image = img;
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView4.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Photo Gallery", #"Photo Gallery");
self.tabBarItem.image = [UIImage imageNamed:#"42-photos.png"];
}
return self;
}
////start of saving////
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
self.array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView4.image)]];
[self.user setObject:self.array forKey:#"images"];
[user synchronize];
}
- (void)viewDidLoad
{
self.user = [NSUserDefaults standardUserDefaults];
NSLog(#"It is %#", self.user);
self.array = [[self.user objectForKey:#"images"]mutableCopy];
imageView.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:0]];
imageView2.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:1]];
imageView3.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:2]];
imageView4.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:3]];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
backToGalleryButton.hidden = YES;
tapToDeleteLabel.hidden = YES;
deleteButton1.hidden = YES;
[super viewDidLoad];
}
///// shows the hidden and invisible "delete" button over each photo.
- (IBAction)editButtonPressed:(id)sender {
grabButton.hidden = YES;
editButton.hidden = YES;
backToGalleryButton.hidden = NO;
tapToDeleteLabel.hidden = NO;
deleteButton1.hidden = NO;
}
////
// This is when the user taps on the image to delete it.
- (IBAction)deleteButtonPressed:(id)sender {
NSLog(#"Sender is %#", sender);
UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:#"Delete"
message:#"Are you sure you want to delete this photo?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[deleteAlertView show];
int imageIndex = ((UIButton *)sender).tag;
deleteAlertView.tag = imageIndex;
}
- (void)alertView: (UIAlertView *) alertView
clickedButtonAtIndex: (NSInteger) buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex]) {
NSLog(#"User Clicked Yes. Deleting index %d of %d", alertView.tag, [array count]);
NSLog(#"The tag is %i", alertView.tag);
[self.array removeObjectAtIndex: alertView.tag];
NSLog(#"After deleting item, array count = %d", [array count]);
NSLog(#"Returned view is :%#, in view: %#", [self.view viewWithTag:alertView.tag], self.view);
((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil;
}
[self.user setObject:self.array forKey:#"images"];
}
Update: I added breakpoints, and discovered that ((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil; is the line that is causing the crash, I still can't figure out why though.
I don't see setImage: being called in any of that code you pasted into your question, but I can tell you this: UIButton's setImage method actually requires a button state as a second parameter.
e.g.
[UIButton setImage: forState:]
I've linked Apple's documentation for you.
If your calls to setImage: don't have a second parameter, that would explain the "unrecognized selector error" you're seeing.
This is an extension to #MichaelDautermann , who makes a very good point that a UIButton has a setImage:forState: method, but no setImage method. However, you ARE calling set image. Not on a button, but when you say imageView.image and imageView2.image. This invokes the setter method for the image view. I would set a breakpoint (or use NSLog and the %# item) to ensure that imageView is in fact an imageView and not a button. If it somehow changed from under you, this could be causing the issue. Simply set a break point at those two lines and see if you even make it past them.
Additionally, if Xcode isn't popping you over to which line is actually causing the issue, check your crash logs. Symbolicated, the log will give you the line number. Or, a less direct approach would be to set breakpoints at the ends of the methods you provided in your answer, and see how many you get past. Once you crash, you can narrow down which method is causing you grief, and then start setting break points within the method until you get to the line in question.
UPDATE:
You said in the comments that ((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil is what you have, but self.view is a UIControl. Changing the cast won't affect the result. Is the UIImageView whose image you are trying to delete a subview of a UIControl? If not, you're never going to get the image view back from viewWithTag. Generally, self.view refers to a view controller's view, so if you're getting a UIControl, my assumption is either your whole view is a UIControl, or you're doing this in the wrong class. Which class are you doing this in, and what is it a subclass of? It appears you are doing this in a view controller, but I just want to be sure. And again, in either case (UIControl or UIImageView), neither class responds to setImage.

Saving video after tapping use in UIImagePickerController

I am have an app that calls UIImagePickerController, and you take video using the controller. After you take the video, I want the video to be saved into the camera roll after clicking use in the UIImagePickerController.
Can you help me?
Oh, and UIImagePickerController comes with the option to switch to video, default photo. I want this to be only video, not photo.
My current code is :
picker.sourceType=UIImagePickerControllerCameraCaptureModeVideo;
picker.mediaTypes=[UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
Once you have implemented the following delegate method, pressing the 'Use' button will save your recorded video to the camera roll.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSURL *recordedVideoURL= [info objectForKey:UIImagePickerControllerMediaURL];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:recordedVideoURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:recordedVideoURL
completionBlock:^(NSURL *assetURL, NSError *error){}
];
}
[library release];
}
Use the following to set the capture mode to video :
imagePickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
Here is the complete code that I use to launch the image picker for video recording :
UIImagePickerController * imagePickerController = [[UIImagePickerController alloc] init];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
imagePickerController.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePickerController.showsCameraControls = YES;
imagePickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
imagePickerController.delegate = self;
imagePickerController.cameraDevice = UIImagePickerControllerCameraDeviceFront;
[self presentModalViewController:imagePickerController animated:YES];
}
I was trying to save the video using the absoluteURL of the URL but it didn't work
NSURL *mediaURL = [info objectForKey: UIImagePickerControllerMediaURL];
UISaveVideoAtPathToSavedPhotosAlbum(mediaURL.absoluteString, self, #selector(video:didFinishSavingWithError:contextInfo:), nil);
It finally worked using the path property:
UISaveVideoAtPathToSavedPhotosAlbum([mediaURL path], self, #selector(video:didFinishSavingWithError:contextInfo:), nil);
Hope it helps anyone.
From the UIImagePickerControllerDelegate class reference:
To save a still image to the user’s Camera Roll album, call the
UIImageWriteToSavedPhotosAlbum function from within the body of the
imagePickerController:didFinishPickingMediaWithInfo: method. To save a
movie to the user’s Camera Roll album, instead call the
UISaveVideoAtPathToSavedPhotosAlbum function. These functions,
described in UIKit Function Reference, save the image or movie only;
they do not save metadata.
Your UIImagePickerControllerDelegate may look something like this...
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey:#"mediaType"];
if (![mediaType isEqualToString:kUTTypeMovie])
return;
NSURL *mediaURL = [info objectForKey:#"mediaURL"];
UISaveVideoAtPathToSavedPhotosAlbum(mediaURL.absoluteString, self, #selector(video:didFinishSavingWithError:contextInfo:), NULL);
}
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo: (void *)contextInfo
{
if (!videoPath && error)
{
NSLog(#"Error saving video to saved photos roll: %#, %#", error, [error userInfo]);
// Handle error;
return;
}
// Video was saved properly. UI may need to be updated here.
}
Notice that I've implemented the completion selector for UISaveVideoToSavedPhotosAlbum(). If it were to fail for any reason, the error would be reported here and you can then handle it appropriately.

UIImage Causing Views All Throughout the iPhone to Turn Black

Alright, this problem is rather bizarre, and I'm 99% positive at this point it's not a retain/release error. Basically I'm using UIImagePickerController to grab an image from either the library or the camera, but the resulting image causes some... strange things to happen. Though it's laggy to pull the image up in a detail view controller containing a UIImageView, the real problem is that if I repetitively pull up the view controller and dismiss it, eventually parts of the image will have disappeared!!! More disturbingly, if I attempt to pull it up again after this has happened, the whole image will have turned black, and other UIViews throughout both my App AND the iPhone itself will either flicker wildly or have turned black!!!! For example, both the iPhone wallpaper and the initial "slide to unlock" panel turn black and flicker, respectively... It makes no difference whether the photo came from the library or the camera. The problem is entirely averted (no lag, no blackness) if I do something like this:
//UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
#"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
Thus I can't imagine the problem having to do with anything other than the UIImagePickerController. Note that I'm still creating and dismissing it-- I'm just not grabbing the image out of it.
At first I thought the image was simply being overreleased, and the disappearing black chunks were parts of memory being overwritten. However, changing the image's retain count makes no difference. Then I thought, maybe the image is too large and somehow not getting properly released when I dismiss the UIImageView! Yet downloading a several MB image from the internet will not replicate the error. Then I thought, maybe the UIImagePickerController is disposing of the image, regardless of retain count! But copying the image failed as well. Furthermore, how could any of these things effect views that exist as deep as the iOS level?! I've researched, experimented, and Googled and no one has encountered this problem except for me... yet I'm not doing particularly strange! Is this an Apple issue? Did I forget something obvious? I've scanned up and down the documentation and guides to no avail, but perhaps I missed something.
None of this has worked:
Incrementing the retain count
Using [image copy]
None of this has replicated the problem with the downloaded image:
Decrementing the retain count
Downloading an image of size greater than 1 MB with large dimensions
I'm using the latest Verizon iPhone with iOS 4.2.8 (base SDK "overriden" to 4.3, whatever that means). 4.2.8 is the latest possible one for Verizon, though 4.3 is available for iPhones using AT&T.
Here's the code in glorious detail. I'm not yet checking for device compatibility, but it shouldn't matter concerning this. Perhaps I forgot some setting with the UIImagePickerController?
Quick Overview: I display an action sheet, then based on the user's input, display the image picker. I save the image as a transformable attribute on a Core Data object (delivery) using a custom transformer. I later hand the image to a detail view controller to display to the user.
IGDeliveryVC.m (parts of it, anyways. It's a tableview displaying the delivery's added media)
- (void)refresh
{
[mediaDisplayArray release];
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"displayIndex" ascending:YES];
mediaDisplayArray = [[NSMutableArray alloc] initWithArray:
[delivery.deliveryMedia sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]]];
if (mediaDisplayArray == nil)
mediaDisplayArray = [[NSMutableArray alloc] init];
[self.tableView reloadData];
}
- (void)onAddMedia:(id)sender
{
#if TARGET_IPHONE_SIMULATOR
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
#"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
[self refresh];
[image release];
return;
#endif
UIActionSheet *options = [[UIActionSheet alloc] initWithTitle:#"Add Media from..."
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Camera", #"Library", nil];
[options showFromToolbar:self.navigationController.toolbar];
[options release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0 && buttonIndex != 1)
return;
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.mediaTypes = [NSArray arrayWithObjects:#"public.image",#"public.movie", nil];
switch (buttonIndex)
{
case 0:
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
break;
case 1:
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
break;
}
[self presentModalViewController:picker animated:YES];
}
- (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker
{
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString* mediaType = (NSString*)[info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:#"public.image"])
{
UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
//UIImage* image = [[UIImage alloc] initWithData:
// [NSData dataWithContentsOfURL:
// [NSURL URLWithString:
// #"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
}
else if ([mediaType isEqualToString:#"public.movie"])
{
NSString* videoURL = (NSString*)[info objectForKey:UIImagePickerControllerMediaURL];
[delivery addVideo:videoURL];
}
else
{
NSLog(#"Error: imagePickerController returned with unexpected type %#", mediaType);
}
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
IGMedia* media = [mediaDisplayArray objectAtIndex:indexPath.row];
UIViewController* detailViewController =
[media isMemberOfClass:[IGMediaImage class]]
? [[IGMediaImageDetailVC alloc] initWithImage:((IGMediaImage*)media).image]
: nil;
if (detailViewController != nil)
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
IGMediaImageDetailVC.h (I use a xib :P )
#import <UIKit/UIKit.h>
#interface IGMediaImageDetailVC : UIViewController {
}
#property (nonatomic, retain) UIImage* image;
#property (nonatomic, retain) IBOutlet UIImageView* imageView;
- (id)initWithImage:(UIImage*)anImage;
#end
IGMediaImageDetailVC.m
#import "IGMediaImageDetailVC.h"
#implementation IGMediaImageDetailVC
#synthesize image;
#synthesize imageView;
- (id)initWithImage:(UIImage*)anImage
{
self = [super initWithNibName:#"IGMediaImageDetailVC" bundle:nil];
if (self)
{
self.image = anImage;
}
return self;
}
- (void)dealloc
{
[image release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView.image = self.image;
}
- (void)viewDidUnload
{
[super viewDidUnload];
[imageView release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
If there's anything I can do to make this post more legible, please let me know. I'll add things that don't work/replicate the problem to the appropriate list. Thanks for taking the time to read this!
I just figured out the problem, had to do with excessive memory use. Thanks goes to this amazing post: https://stackoverflow.com/questions/1282830/uiimagepickercontroller-uiimage-memory-and-more Basically, before ever displaying the image, I divide each dimension by 4, resulting in a total 1/16th of the memory I was using before. Here's the method I used (as per the awesome post):
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
In the method call, I pass in CGSizeMake(image.size.width/4, image.size.height/4)
Hope this was helpful!