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. Here is the code 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.");
[self.array removeObjectAtIndex: alertView.tag];
}
[self.user setObject:array forKey:#"images"];
}
The issue here is that when I click "Yes" in the alert view, nothing happens. However, if I tap on the image and click "Yes" a second time, the app crashes, and the debug states:
Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray removeObjectAtIndex:]: index (0) beyond bounds (0)' So, I'm not sure where to go from here, I'm still very new to programming and everything looks correct to me. Any help is much appreciated, thanks!
Here is how I add them into the array:
////start of saving////
- (void)viewWillAppear:(BOOL)animated
{
self.user = [NSUserDefaults standardUserDefaults];
self.array = [[self.user objectForKey:#"images"]mutableCopy];
while(self.array == nil)
{
[self.user setObject:[NSMutableArray arrayWithObject:#""] forKey:#"images"];
self.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);
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.array addObject:[NSData dataWithData:UIImagePNGRepresentation(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 = [[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]];
imageView5.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:4]];
imageView6.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:5]];
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];
}
- (void)viewDidUnload
{
self.user = nil;
}
////end of saving
///// 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;
}
- (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.");
NSLog(#"Array: %#, index: %d", self.array, alertView.tag);
[self.array removeObjectAtIndex: alertView.tag];
}
[self.user setObject:array forKey:#"images"];
}
#end
EDIT: This is the code I use to put the objects into the UIImageView from the users camera roll:
- (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;
}
if (imageView4.image == nil) {
imageView4.image = img;
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
}
When I added NSLog(#"Array: %#, index: %d", self.array, alertView.tag); just before removeAtIndex the console says 2012-04-03 18:39:39.066 AppName[1631:f803] Array: (null), index: 0. Could that be the cause? I'm not sure why it would be, I think the code looks fine.
Removing the image from the array is not the only step you have to take. Your code is correct for removing the image from the array, which is why you get image out of bounds the second time, but you also need to remove the image from the UI so the user can no longer delete an image that is not there.
There are many oddities with this code but I think the problem is that you are not calling super on your viewwillappear and viewdidload functions. I would get rid of your viewWillAppear method as it serves no purpose.
Related
I'm testing my ios app that has a deployment target of 5.0 and a base SDK of 6.1.
Everything works fine in ios 5.0, and ios 5.1, but in ios 6.0 I'm having an issue with inserting a subview at index. The subview is a tableview and the parent view is an uialertview that was created as a special class "UIAlertTableView." The alertview appears, but there appears to be nothing inserted. Before this, I had fixed an autorotation issue of the superview (which is in landscape) because, as it is well known ios 6.0 handles rotation differently, so now my superview appears correctly, but as I said, this alertview pops up with no table now. Am I suppose to be fixing autorotation issues for the tableview as well as the superview? I didn't think this would be necessary since the tableview is not declared in the imported class, is is declared within the parent viewcontroller. Or could this be because of some method that was deprecated in ios 6.0?
/*UIAlertTableView.m (the imported object class)*/
#interface UIAlertView (private)
- (void)layoutAnimated:(BOOL)fp8;
#end
#implementation UIAlertTableView
#synthesize tableWidth;
#synthesize tableHeight;
#synthesize lowestView;
#synthesize kTablePadding;
#synthesize alertDelegate;
- (void)layoutAnimated:(BOOL)fp8 {
[super layoutAnimated:fp8];
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y - tableExtHeight/2, self.frame.size.width, self.frame.size.height + tableExtHeight)];
// We get the lowest non-control view (i.e. Labels) so we can place the table view just below
int i = 0;
while (![[self.subviews objectAtIndex:i] isKindOfClass:[UIControl class]]) {
lowestView = [self.subviews objectAtIndex:i];
i++;
}
tableWidth = 262.0f;
for (UIView *sv in self.subviews) {
// Move all Controls down
if ([sv isKindOfClass:[UIControl class]]) {
sv.frame = CGRectMake(sv.frame.origin.x, sv.frame.origin.y + tableExtHeight, sv.frame.size.width, sv.frame.size.height);
}
}
}
- (void)show{
[self prepare];
[super show];
}
- (void)prepare {
if (tableHeight == 0) {
tableHeight = 150.0f;
}
kTablePadding = 8.0f;
tableExtHeight = tableHeight + 2 * kTablePadding;
[self setNeedsLayout];
}
#end
/*the UIAlertTableView class is imported into the myViewController header file*/
/*myViewController.m*/
#implementation myViewController
#synthesize myTableView;
#synthesize alert;
#synthesize imageView;
#synthesize scrollView;
#synthesize models;
#synthesize picked;
#pragma myTableView
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [models count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// now configure the cell
cell.textLabel.text = [models objectAtIndex:[indexPath row]];
[cell setAccessibilityTraits: UIAccessibilityTraitButton];
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (returnedSetting && (indexPath.row == prevSelectedIndex)){
returnedSetting = FALSE;
}
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//index path to row that's selected
NSIndexPath *myIndexPath = [myTableView indexPathForSelectedRow];
UITableViewCell *cell = [myTableView cellForRowAtIndexPath:myIndexPath];
labelText = cell.textLabel.text;
selectedModel = cell.textLabel.text;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSIndexPath *myIndexPath = [tableView indexPathForSelectedRow];
prevSelectedIndex = myIndexPath.row;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:myIndexPath];
[alert dismissWithClickedButtonIndex:0 animated:YES];
labelText = cell.textLabel.text;
selectedModel = cell.textLabel.text;
[self dismissModalViewControllerAnimated:YES];
}
-(void)removeButton{
[UIView animateWithDuration:1.5
delay:1.5
options:UIViewAnimationCurveEaseInOut
animations:^ {
eButton.alpha = 0;
}
completion:^(BOOL finished) {
}];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (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.
}
-(void)showAlertFor:(NSTimer *)timer{
[alert show];
myTableView.frame = CGRectMake(11.0f, alert.lowestView.frame.origin.y + alert.lowestView.frame.size.height + 2 * alert.kTablePadding, alert.tableWidth, alert.tableHeight);
[alert insertSubview:myTableView atIndex:1];
[myTableView performSelector:#selector(flashScrollIndicators) withObject:nil afterDelay:.3];
}
-(void)bringupAlertTableViewFor:(NSString *)dName atLocationOnScreen:(CGPoint)newPoint{
if (([dName isEqualToString:#"hello"]){
picked =TRUE;
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f) style:UITableViewStylePlain];
alert = [[UIAlertTableView alloc] initWithTitle:dName
message:#"Choose from the table below:"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert setDelegate: alert.alertDelegate];
models = [[NSMutableArray alloc]init];
myTableView.delegate = self;
myTableView.dataSource = self;
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Decisions" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"dName == %#", dName];
[request setPredicate: predicate];
NSError *error;
//matches found
NSArray *matchingData = [context executeFetchRequest: request error: &error];
for (NSManagedObject *obj in matchingData) {
[models addObject:[NSString stringWithFormat:#"%#",[obj valueForKey: #"model"]]];
}
alert.tableHeight = 120;
[alert show];
myTableView.frame = CGRectMake(11.0f, alert.lowestView.frame.origin.y + alert.lowestView.frame.size.height + 2 * alert.kTablePadding, alert.tableWidth, alert.tableHeight);
[alert insertSubview:myTableView atIndex:1];
[myTableView performSelector:#selector(flashScrollIndicators) withObject:nil afterDelay:.3];
}else{
picked = TRUE;
frame.origin.x = newPoint.x - 29; // new x coordinate
frame.origin.y = 240; // new y coordinate
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration: 1.5];
[UIView commitAnimations];
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f) style:UITableViewStylePlain];
alert = [[UIAlertTableView alloc] initWithTitle:dName
message:#"Select a choice:"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert setDelegate: alert.alertDelegate];
models = [[NSMutableArray alloc]init];
myTableView.delegate = self;
myTableView.dataSource = self;
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Decisions" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deName == %#", dName];
[request setPredicate: predicate];
NSError *error;
//matches found
NSArray *matchingData = [context executeFetchRequest: request error: &error];
for (NSManagedObject *obj in matchingData) {
[models addObject:[NSString stringWithFormat:#"%#",[obj valueForKey: #"model"]]];
}
alert.tableHeight = 120;
[NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(showAlertFor:) userInfo:nil repeats:NO];
previousPoint = newPoint;
}
}
-(IBAction)singleTapImageView:(UITapGestureRecognizer *)sender {
CGPoint pt = [sender locationInView: sender.view];
//find out which was pressed
if ( ((pt.x >= 52) && (pt.x <= 79)) && ((pt.y >= 269) && (pt.y <= 296))){
CGPoint newPoint = {45, (257 + 55)};
[self bringupAlertTableViewFor:#"Choice1" atLocationOnScreen:newPoint];
}
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)sView{
[self removeButton];
}
-(void)scrollViewDidScroll:(UIScrollView *)sView{
eButton.alpha = .7;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)sView
{
[self removeButton];
}
-(void)exitButtonPressed{
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidLoad
{
UIImage *imageS = [UIImage imageNamed:#"ti.png"];
imageView = [[TouchDetectingImageView alloc]initWithImage:imageS];
[imageView setDelegate:self];
imageView.frame = CGRectMake(20, 25, imageS.size.width,imageS.size.height);
CGFloat newScrollWidth = imageView.image.size.width + 20;
[scrollView setContentSize:(CGSizeMake(newScrollWidth, imageView.image.size.height))];
[scrollView addSubview: imageView];
imageView.contentMode = UIViewContentModeScaleToFill;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapImageView:)];
singleTap.numberOfTapsRequired = 1;
[scrollView addGestureRecognizer:singleTap];
UIImage *img = [UIImage imageNamed:#"backbutton.png"];
CGRect frameForButton = CGRectMake(0, 3, img.size.width, img.size.height);
eButton = [[exitButton alloc] initWithFrame:frameForButton];
[eButton setDelegate:self];
[eButton addTarget:self action:#selector(exitButtonPressed) forControlEvents:UIControlEventTouchUpInside];
UITapGestureRecognizer *buttonTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(exitButtonPressed)];
buttonTap.numberOfTapsRequired = 1;
[eButton addGestureRecognizer:buttonTap];
eButton.alpha = 0;
[self.view addSubview:eButton];
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewDidUnload
{
[self setScrollView:nil];
[self setImageView:nil];
[self setmyTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[scrollView flashScrollIndicators];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// The menu is only going to support landscape orientations
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
- (NSInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotate{
return YES;
}
#end
The reason(s) for this were:
1) the call to layoutAnimated was being ignored
2) it appears that ios 6 handles view hierarchy somewhat differently...
3) ios 6.0 automatically scales to fit a 1136x640 display vs earlier version that scale to fit a 960x640 display.
solutions were:
use a layoutsubviews call and layoutifneeded
also using conditional statements to find the ios version (e.g. greater_than_or_equal_to ios 6.0)
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.
I stored images in an NSMutableArray, and now I'm trying to get them to show up in viewDidLoad. I tried calling initWithContentsOfFile, but that doesn't seem to work. This is how the code looks:
imageView.image = [[UIImage alloc] initWithContentsOfFile:[self.array objectAtIndex:0]];
I'm not sure what I should use instead of initWithContentsOfFile to have the saved images load, I'm not even sure if I can save images in a plist through user defaults. I've been researching it for awhile now to no avail. Any help is much appreciated, thanks!
EDIT: Here is additional 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 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;
}
}
- (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.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] initWithContentsOfFile:[self.array objectAtIndex:0]];
imageView2.image = [[UIImage alloc] initWithContentsOfFile:[self.array objectAtIndex:1]];
imageView3.image = [[UIImage alloc] initWithContentsOfFile:[self.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];
}
EDIT: This is how I'm tagging the images and deleting them based upon their tags:
- (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"];
}
The problem is that you can't store images in a property list, which is what you're trying to do when you save it in the user defaults. You need to use an archiver to convert the image to an NSData object which you can store.
It looks like you're not passing a valid image path to the initializer method. Make sure the path is correct, and that it includes the image extension.
Really, though, you shouldn't be calling initWithContentsOfFile: in this case, because UIImageView's image property retains the image when you set it. That will usually lead to a memory leak (unless you're using automatic reference counting). Use one of the static initializers instead, such as imageNamed:, which has the added bonuses of using the system cache and also automatically loading the correct version of the image based on the characteristics of the device (for instance, it will load a higher resolution variant of the image if the device has a retina display).
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.
I'm having an issue with my iOS app freezing whenever I trigger a UIActionSheet with a UIPickerView inside it. The picker wheel scrolls fine until I try to hit the "Done" button on the UIActionSheet, at which point the UI freezes. However, XCode isn't registering any kind of crash in the program, so I'm pretty confused.
Has anyone else run into this problem before? How can I solve it?
I never Face this type of problem.I think this one is hekp you to solve your problem. I used the PickerView in same way
UIActionSheet *actionSheet;
NSString *pickerType;
- (void)createActionSheet {
if (actionSheet == nil) {
// setup actionsheet to contain the UIPicker
actionSheet = [[UIActionSheet alloc] initWithTitle:#"Select"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
UIToolbar *pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
[flexSpace release];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(pickerDone:)];
[barItems addObject:doneBtn];
[doneBtn release];
[pickerToolbar setItems:barItems animated:YES];
[barItems release];
[actionSheet addSubview:pickerToolbar];
[pickerToolbar release];
[actionSheet showInView:self.view];
[actionSheet setBounds:CGRectMake(0,0,320, 464)];
}
}
-(IBAction)BtnPressed:(id)sender
{
[self createActionSheet];
pickerType = #"picker";
select = NO;
UIPickerView *chPicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
chPicker.dataSource = self;
chPicker.delegate = self;
chPicker.showsSelectionIndicator = YES;
[actionSheet addSubview:chPicker];
sessoTxt.text = [sessoArray objectAtIndex:0];
[chPicker release];
}
#pragma mark UIPickerViewDelegate Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
int count;
if ([pickerType isEqualToString:#"picker"])
count = [array count];
return count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *string;
if ([pickerType isEqualToString:#"picker"])
string = [array objectAtIndex:row];
return string;
}
// Set the width of the component inside the picker
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return 300;
}
// Item picked
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
select = YES;
if ([pickerType isEqualToString:#"picker"])
{
Txt.text = [array objectAtIndex:row];
}
}
- (void)pickerDone:(id)sender
{
if(select == NO)
{
if ([pickerType isEqualToString:#"picker"])
{
Txt.text = [array objectAtIndex:0];
}
}
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
[actionSheet release];
actionSheet = nil;
}
}