My app consists of an image gallery. It is a scroll view which displays pics retrieved from the sqlite db. User can scroll the images, add or delete images etc. I can add a picture to the gallery dynamically. But the problem comes when I need to implement the delete functionality. I use the following code but even after calling removeFromSuperView the image is not getting removed from the scrollview.
-(void)deleteDeck{
if(selectedEditDeck!=0){
[deck deleteSelectedDeck:selectedEditDeck]; //deleting from database
//problem starts here ***
[(UIImageView*)[decksGallery viewWithTag:selectedEditDeck-1]removeFromSuperview];
[self loadGallery];
selectedEditDeck=0;
//Ends here*****
[tableData release];
tableData=[NSMutableArray array];
[self showCardNamesinTable];
[aTableView reloadData];
}
I have already created the uiscrollview in the loadview method. Then to refresh the view after every deletion and addition of images so that I can display the updated gallery, I use the following piece of code:
-(void)loadGallery{ //Reloading all for adding a single deck image.
//Database part****
NSString *sqlStr = [NSString stringWithFormat:#"select first_card from decksTable"];
char *sql = (char*)[sqlStr UTF8String];
kidsFlashCardAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSMutableArray *galleryImagesArray=[appDelegate.dbConnection fetchColumnFromTable:sql col:0];
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *docDirectory = [sysPaths objectAtIndex:0];
numberofImages = [galleryImagesArray count];
printf("number is %d",numberofImages);//Database part of code ends here
//In the following fragment of code I add images to UIscrollView
for (int i = 0; i < [galleryImagesArray count]; i++) {
CGFloat yOrigin = i * 65;
NSString *filePath = [NSString stringWithFormat:#"%#/%#", docDirectory,[galleryImagesArray objectAtIndex:i]];
galleryImage = [[UIImageView alloc] initWithFrame:CGRectMake(yOrigin+140, 15,50,50 )];
//galleryImage.tag=[galleryImagesArray count];
galleryImage.tag=i;
printf("THE TAG IS %d",galleryImage.tag);
galleryImage.clipsToBounds=YES;
galleryImage.layer.cornerRadius=11.0;
galleryImage.backgroundColor=[UIColor clearColor];
galleryImage.image =[UIImage imageWithContentsOfFile:filePath];
[decksGallery addSubview:galleryImage];
[galleryImage release];
}
decksGallery.contentSize = CGSizeMake(115*[galleryImagesArray count], 80);
//[decksGallery reloadInputViews];
Make sure your [decksGallery viewWithTag:selectedEditDeck-1] is not returning nil and the image was actually deleted from your db before the refresh code running.
In addition, you are setting imageView.tag = i; in your creation code, since the i would be 0 which is the tag's default value for every UIView, you'd better fix your creation code as well.
If you just want to remove the image from your imageView, you can also do imageView.image = nil;
UIImageView*imageView = (UIImageView*)[decksGallery viewWithTag:selectedEditDeck-1];
[imageView removeFromSuperview];
imageView = nil;
Related
I have a sprite sheet in my documents directory. The sprites sheet consists of 256 icons.
I have a table view, in which for each row I am creating 3 buttons with background images from the sliced part of the sprite sheet.
Below is the code to extract the sliced icon from sprite. Using the coordinates known
-(UIImage *)getSlicedImageForDictionary:(NSDictionary *)spriteInfoDictionary spriteImageRef:(UIImage *)spriteImageReference{
NSString *frameString = [spriteInfoDictionary objectForKey:#"frame"];
NSString *frameStringWithOnlyCommas = [[[frameString stringByReplacingOccurrencesOfString:#"\"" withString:#""] stringByReplacingOccurrencesOfString:#"{" withString:#""] stringByReplacingOccurrencesOfString:#"}" withString:#""];
NSArray *frameValues = [frameStringWithOnlyCommas componentsSeparatedByString:#","];
CGImageRef cgIcon = CGImageCreateWithImageInRect(spriteImageReference.CGImage, CGRectMake([[frameValues objectAtIndex:0] floatValue], [[frameValues objectAtIndex:1] floatValue], [[frameValues objectAtIndex:2] floatValue], [[frameValues objectAtIndex:3] floatValue]));
UIImage *icon = [UIImage imageWithCGImage:cgIcon];
frameString = nil;
frameStringWithOnlyCommas = nil;
frameValues = nil;
CGImageRelease(cgIcon);
return icon;
}
I am calling the above method in the for loop as below,
for (int i = 0; i < [iconInfoArray count]; i ++) {
NSDictionary *currentBook = [spriteInfoDictionary objectForKey:[iconInfoArray objectAtIndex:i]];
articleMetaData.articleImage = [self getSlicedImageForDictionary:currentBook spriteImageRef:self.spriteImageSource];
[spriteIconsArray addObject:articleMetaData];
}
The problem is,
When I set the buttons in the table view from the spriteIconsArray I am getting the "Received memory warning" problem and the application crashes.
[btn setBackgroundImage:[spriteIconsArray objectAtIndex:i] forState:UIControlStateNormal];
But when I write the image reference to the documents directory I am not getting any memory warnings and the application runs smoothly.
NSData *data = UIImagePNGRepresentation([spriteIconsArray objectAtIndex:i]);
NSString *pathOfTempImage = [[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:#"tempIcon.png"];
[data writeToFile:pathOfTempImage atomically:YES];
[btn setBackgroundImage:pathOfTempImage forState:UIControlStateNormal];
I am confused why this is happening. I don't want to write the image to the documents and read it again, which may create some latency for large set of data.
Please suggest me in this.
Thanks
In my app, I use UIImageView and UIScrollView to show a lot of images (every time there are about 20 images and every image is about 600px*500px and size is about 600kb).
I use this code to accomplish this function:
// Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
imageData = nil;
iv = nil;
iv.image = nil;
filePath = nil;
[imageData release];
[filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
But every time I initialize this function, the memory will increase about 5MB. Actually I release UIImageView, UIimage and UIScrollView (vi.image=nil, [vi release]) but it doesn't work, the allocated memory is not getting released. BTW, I used my friend's code first vi.image = nil then vi = nil; but the pictures are not getting displayed on scrollview.
The main problem, as I see it, is that you're setting your local variables to nil and then you proceed to try to use those local variables in methods like release and the like, but because they've been set to nil, those methods now do nothing, and the objects with the +1 retainCount (or now +2 because you've added them to your view) are never released.
Thus, I'd suggest the following:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
// Don't set these to nil, or else subsequent release statements do nothing!
// These statements are actually not necessary because they refer to local
// variables so you don't need to worry about dangling pointers. Make sure
// you're not confusing the purpose of setting a pointer to nil in ARC to
// what you're doing in your non-ARC code.
//
// imageData = nil;
// iv = nil;
// You don't want to set this to nil because if iv is not nil, your image
// will be removed from your imageview. So, not only is this not needed,
// but it's undesired.
//
// iv.image = nil;
// This statement is not necessary for the same reason you don't do it
// to imageData or iv, either. This is probably even worse, though, because
// filePath is not a variable that you initialized via alloc. You should
// only be releasing things you created with alloc (or new, copy, mutableCopy,
// for which you issued a retain statement).
//
// filePath = nil;
[imageData release];
// filePath is a +0 retainCount already, so don't release. You only release
// those items for which you increased retainCount (e.g. via new, copy,
// mutableCopy, or alloc or anything you manually retained).
//
// [filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Thus, your code would be simplified (and corrected) to probably just be:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
[imageData release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Or you could further simplify your code through the use of autorelease:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)] autorelease];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
By the way, the statement (with autorelease):
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
could probably be simplified to:
UIImage *imageData = [UIImage imageWithContentsOfFile:filePath];
This gives you a UIImage, with a +0 retainCount (i.e. you don't have to release it) from your file.
So, a few final observations:
You really should probably review and study the Advanced Memory Management Programming Guide. It's dense reading if you're new to memory management, but mastery of these concepts (especially in non-ARC code) is critical.
If you haven't, I'd encourage you to run your code through the static analyzer ("Product" - "Analyze" or shift+command+B). I'd be surprised if many (if not most) of these issues wouldn't have been identified for you by the analyzer. You should have zero warnings when you run your code through the analyzer.
If you want to take this to the next level, you might want to be far more conservative about your memory management. The governing principle would be a system design that only loads the images that are needed at the UI at any given time, which involves not only calvinBhai's excellent suggestion of lazy loading of images (i.e. don't load images into memory until your UI really needs them), but also a pro-active releasing images once they've scrolled off the screen. Maybe you don't need to worry about it in your app, because you're only dealing with 20 images at a time, but if any of your portfolios/galleries expanded to 100 or 1000 images, the idea of keeping all of those in memory at any given time is just a bad idea. This is a more advanced concept, so maybe you should focus on the basic memory management problems of your existing code first, but longer term, you might want to contemplate lazy loading and pro-active releasing of images.
If memory is your concern,
try lazy loading the images = Load the visible image, the next and previous image. You dont have to have all images added to your klpscrollview.
Once you figure out lazy loading the images onto your scrollview, then you can think of fixing the images not showing on your scrollview.
Easier would be to search for "lazy load uiimage uiscrollview"
I'm currently trying the iCarousel Multiple Carousel example.
Here in my array, I've add images with NSMutableDictionary:
I have two of these: ( myImages and myImages2 for my two slot in carousel loaded in ViewDidLoad)
self.myImages = [NSMutableArray array];
for(int i = 0; i <= 10; i++)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedPath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"myImages%d.png", i]];
if([[NSFileManager defaultManager] fileExistsAtPath:savedPath]){
NSMutableDictionary *container = [[NSMutableDictionary alloc] init];
[container setObject:[UIImage imageWithContentsOfFile:savedPath] forKey:#"image"];
[container setObject:[NSNumber numberWithInt:i] forKey:#"index"];
[images addObject:container];
[container release]; // if not using ARC
}
}
in my iCarousel:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
if (carousel == carousel1)
{
NSDictionary *obj = [items1 objectAtIndex:index];
view = [[UIImageView alloc] initWithImage:[obj objectForKey:#"items1"]];
view.tag = index;
}else
{
NSDictionary *obj = [items2 objectAtIndex:index];
view = [[UIImageView alloc] initWithImage:[obj objectForKey:#"items2"]];
view.tag = index;
}
return view;
}
In another View, these arrays are also loaded, the user has a chance to pick an image to compare to with,
when an user pick an image an int equivalent to its tag is pass to where my two Carousel is.
Here is how I compare them:
NSInteger image = [prefs integerForKey:#"image"];
NSInteger image1 = [prefs integerForKey:#"image2"];
if (image == [(UIImageView*)[self.carousel1 currentItemView] tag] || image2= [(UIImageView*)[self.carousel2 currentItemView] tag] || ) {
I delete an index this way:
NSInteger index = carousel1.currentItemIndex;
[carousel1 removeItemAtIndex:index animated:YES];
[items1 removeObjectAtIndex:index];
I think I'm deleting it the wrong away, because the index arent updated, what i wanted is to maintain its index not adjust like this images right here:
Deleting in NSMutableArray
If I understand what you are trying to do now, which is to move the two carousels until the middle one matches, then to not have the indexes simply go away, then you could just rather than removing the item from the array, fill the location you are trying to delete with a nil image (or a valid image with the backing of your choosing), and a tag stating it has already been matched.
This is all of course dependent on whether my understanding of what you want is valid.
I'm using the Custom Picker by ray, when I press a button a picker is pop-ups then when image is chosen it will dismiss the picker and displayed the image in UIImageView. I put in the viewWillAppear but when I go to another ViewController the image chosen disappears. How to maintain the chosen in UIImageView to be displayed even after going to different View Controllers, only will it change if another image is chosen again.
- (void)viewWillAppear:(BOOL)animated {
secondView.image = _imagePicker.selectedImage;
}
- (IBAction)chooseCustomImageTapped:(id)sender {
_imagePicker = [[CustomImagePicker alloc] init];
_imagePicker.title = #"Choose Custom Image";
for(int i = 0; i <= 10; i++)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Images%d.png", i]];
if([[NSFileManager defaultManager] fileExistsAtPath:savedImagePath]){
[_imagePicker addImage:[UIImage imageWithContentsOfFile:savedImagePath]];
}
}
[self presentModalViewController:_imagePicker animated:NO];
}
I think you should take your image in UIImage object just after selection done UIImage *image = _imagePicker.selectedImage;, and retain it till next change. Then set that image to your image view secondView.image = image;.
Did you alloc a new view controller when you go to the specified view controller? If so, when you go to a new view controller, you alloc it, so all the datas has been initalized.
use the pickerview delegate function.
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
// put your logic here.
NSUserDefaults *udf=[NSUserDefaults standardUserDefaults];
[udf setObject:#"Imagename" forKey:#"LastSelectImage"];
}
accordingly your image has change your image name here.
I am trying to updating UILabel text in a loop in every iteration but it is displaying only the last value whether there is time taken for completing the loop is about 30 -50 sec.
Here is the code:
for (float i=0; i< [topicNew count]; i++) {
NSDictionary *new= [topicNew objectAtIndex:i];
NSString *imageName = [[[NSString alloc] initWithFormat:#"%#.%#.%#.png", appDelegate.subject,topicNamed,[new objectForKey:kWordTitleKey]] autorelease];
NSString *imagePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:imageName];
NSData *imageData = [self ParsingImagePath:[new objectForKey:kWordImagePathKey]];
[progressView setProgress:i/[topicNew count]];
[self setProgress:i/[topicNew count]];
[self.lbltest setText:#"Image Downloading..."];
self.lbltest.text =imageName;
//NSLog(#"sending %f",i/[topicNew count]);
//lblpercent.text = [NSString stringWithFormat:#"%d",i];
[lblpercent setText:[[NSString stringWithFormat:#"%.0f",i/[topicNew count]] stringByAppendingString:#"%"]];
//[self.view addSubview:viewalert];
[self updateLabel:self];
NSLog(#"%#,%d",imageName,i);
if(imageData != nil)
[imageData writeToFile:imagePath atomically:YES];
else
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:NULL];
NSMutableDictionary *newWord = [NSMutableDictionary dictionaryWithObjectsAndKeys:[new objectForKey:kWordTitleKey], kWordTitleKey, [new objectForKey:kWordDefinitionKey], kWordDefinitionKey, imagePath, kWordImagePathKey, appDelegate.subject,kSubjectKey,topicName,kTopicKey,[new objectForKey:kWordMemorizedKey], kWordMemorizedKey, nil];
[newTopic addObject:newWord];
}
I think the problem is your thread is lock by your loop. So you need to perform your method in background and use
[self performSelectorInBackground:#selector(myMethod) withObject:nil];
Don't forget to put in "myMethod";
-(void)myMethod{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//I do my label update
[pool release];
}
The way you are using it now, replaces the text indeed. I don't know which label you'd like to update. If you want to update, for example, lbltest with what's being downloaded all the time, you should change self.lbltest.text = imageName; to [self.lbltest setText:#"%# %#", self.lbltest.text, imageName];
That way, your old text doesn't get replaced by your new text, but your new text gets added to your old text.
Using labelName.text = #"Something"; changes the text on that label to Something.
The same goes for [labelName setText:#"Something"];.
Whenever you use any of the two described above, you'll replace the text in that label with the text "Something". If you want to add something to that label, you have to include the old text in your new String.