Issue with tags in Xcode - objective-c

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];
}
// ...
}

Related

App Doesn't Behave Correctly On Relaunch

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.

Saving Images In iOS

I have an app that behaves like a photo gallery. I'm implementing the ability for the user to delete the photos, by placing an invisible button over each UIImageView, and calling removeObject when they tap on the button. This code is working great, but its dependent upon tags. I need to tag every UIImageView / UIButton in interface builder in order for this to work. So, I'm now trying to save the images in a way that my tags will still work, which excludes using NSData.
So I'm totally lost on what to do right now. I'm very, very new to programming and am shocked that I even made it this far. Any help or advice on what or how to edit my code to make this work is much appreciated, thanks!
Here is my entire file just for reference:
- (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 addObject: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:imageView2.image];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView3.image == nil) {
imageView3.image = img;
[self.array addObject:imageView3.image];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView4.image == nil) {
imageView4.image = img;
[self.array addObject:imageView4.image];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView5.image == nil) {
imageView5.image = img;
[self.array addObject:imageView5.image];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
[self.array addObject:imageView.image];
[self.array addObject:imageView2.image];
[self.array addObject:imageView3.image];
[self.array addObject:imageView4.image];
[self.array addObject:imageView5.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 = [[self.array objectAtIndex:0] copy];
imageView2.image = [[self.array objectAtIndex:1] copy];
imageView3.image = [[self.array objectAtIndex:2] copy];
imageView4.image = [[self.array objectAtIndex:3] copy];
imageView5.image = [[self.array objectAtIndex:4] copy];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
[super viewDidLoad];
}
// 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;
}
- (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
{
if (buttonIndex != [alertView cancelButtonIndex]) {
NSLog(#"User Clicked Yes. Deleting index %d of %d", alertView.tag, [array count]);
NSLog(#"The tag is %i", alertView.tag);
UIImageView *view = [self viewForTag:alertView.tag];
if (view) {
[self.array removeObject:view];
}
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"];
}
#end
It seems you are using the tag just so you can identify which image view is being selected. You have an "invisible" button on top of each image? Is that right? I assume that is so you can handle a tap, which selects the image that is showing through the button?
There are lots of ways to do it, but a simple solution is to just recognize the tap, and "find" the image view underneath that tap. Drop a UITapGestureRecognizer onto your controller from within the storyboard. Ctrl-drag it into the code for your controller, and it will create an action method. Fill it in something like this...
- (IBAction)tapGesture:(UITapGestureRecognizer*)gesture
{
CGPoint tapLocation = [gesture locationInView: self.galleryView];
for (UIImageView *imageView in self.galleryView.subviews) {
if (CGRectContainsPoint(imageView.frame, tapLocation)) {
// This is the imageView that was tapped on!
// Do whatever you want with it now that you found it.
}
}
}
The previous comment is accurate; please think about how you're going to scale this up.
That said, you'll need to store and load images in some way, and that will always mean converting your UIImage objects to NSData. This applies whether you store them in NSUserDefaults, in files or in Core Data.
UIImage supports NSCoding, so you could use that. Read about how to use it, since you'll need it eventually. Or, if you know you'll always want to use PNG or JPEG formats, there are the functions UIImagePNGRepresentation(UIImage *image) and UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality). To convert the NSData objects back to UIImage, use [UIImage imageWithData:NSData*] Here's what you could have in applicationDidEnterBackground:
NSMutableArray *dataArray = [NSMutableArray array];
[dataArray addObject:UIImagePNGRepresentation(imageView.image)];
[dataArray addObject:UIImagePNGRepresentation(imageView2.image)];
[dataArray addObject:UIImagePNGRepresentation(imageView3.image)];
[self.user setObject:dataArray forKey:#"images"];
[self.user synchronize];
Then, to retrieve these during viewDidLoad:
[self.array removeAllObjects];
NSArray *dataArray = [self.user objectForKey:#"images"];
for (NSData *imageData in dataArray)
[self.array addObject:[UIImage imageWithData:imageData]];
This will solve your immediate problem, but please don't consider it a permanent solution. Consider:
Store your images in files or Core Data, as NSUserDefaults is not
supposed to have large amounts of content.
Use a UITableView to display the images, so you can have an
arbitrary number instead of hard-coding for three.
If UITableView doesn't have the layout you need, create a custom
UIView subclass that displays an array of images and responds to taps appropriately.
Make sure your application can detect all the ways it may be
suspended or shut down. You may not always get the
ApplicationDidEnterBackground notification, or you may not have time
to save all your data after it happens. If you have multiple
UIViewControllers, this one may be unloaded without the application
itself receiving notifications.

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.

Resign first responder from separate class

I have a class that makes a keyboard toolbar which has "Next", "Previous", and "Done" buttons on it. Is there a way for this class to know (or find out) what objects are on the screen at any time?
E.g., can it see what the current view is and what the text fields on it are, and then be able to resign the first responder?
If you specifically want to resign first responder without the need to known which view is the first responder you can send resignFirstResponder to "nil" like this:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
This is documented behaviour although I cannot find in the docs right now.
Is there a way for this class to know
(or find out) what objects are on the
screen at the time?
Find the momma view and you can iterate through all the objects on the screen (because they will be UIViews too) like this. Note that you may need to add recursion:
for (UIView *view in mommaView.subviews) {
do something to the view
}
You can start at the Window class and go down from there, asking [view respondsTo:#selector(isFirstResponder) && [view isFirstResponder] on each. Some debugging code that I use might come in handy as a template and also while debugging:
+ (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews:[[UIApplication sharedApplication] keyWindow] from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent)
{
Class cl = [view class];
NSString *classDescription = [cl description];
// while ([cl superclass]) //restore to print superclass list
// {
// cl = [cl superclass];
// classDescription = [classDescription stringByAppendingFormat:#":%#", [cl description]];
// }
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
+ (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
+ (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
yes, the methods provided below will be called whenever a textField becomes Active. I think you are looking for
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return 1;
}
or
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
- (void) textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
and if you are looking for a specific textField in your view, you should assign them tags:
textField.tag =1 // for textField 1
textField.tag =2 // for textField 2
// You may check for these tags and then resign specific ones.