One of the features of my application is to allow users to save images. These images are later presented in a detail view of a UITableView and in the cells of the UITableView. However, when returning to the page that is displaying the cells it takes a noticeably long time for it to load. I believe this is because the images are saved to the documents directory and every time the page loads they are reloaded from the directory. Should I be storing loading the images into an memory every time the application launches or am I way off in my theory. I have also tried compressing the images with the following code and it did not increase performance.
[[GTImageStore sharedStore] setImage:currentImage forKey:currentImageKey];
NSString *jpgName = [[NSString alloc] initWithFormat:#"Documents/%#.jpg", currentImageKey];
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:jpgName];
float scale;
if (currentImage.size.width < currentImage.size.height)
{
scale = 100/currentImage.size.width;
}
else if (currentImage.size.width > currentImage.size.height)
{
scale = 100/currentImage.size.height;
}
else
{
scale = 100/currentImage.size.width;
}
UIImage *image = [[UIImage alloc] initWithCGImage:currentImage.CGImage scale:scale orientation:[currentImage imageOrientation]];
[UIImageJPEGRepresentation(image, 0.5) writeToFile:jpgPath atomically:YES];
Any help is really appreciated! Thank you!
Code for adding cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UINib *giftCell = [UINib nibWithNibName:#"GiftCell" bundle:nil];
[tableView registerNib:giftCell forCellReuseIdentifier:#"UIGiftsTableCell"];
cell = [tableView dequeueReusableCellWithIdentifier:#"UIGiftsTableCell"];
if (!cell) {
cell = [[GTGiftCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"UIGiftsTableCell"];
}
// Determine gift at index path
GTGift *gift = (GTGift *)[[[GTGiftStore sharedStore] allGifts] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[gift name]];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterLongStyle];
NSString *subText = [[NSString alloc] initWithFormat:#"Purchased on %#", [formatter stringFromDate:[gift datePurchased]]];
[[cell subTextLabel] setText:subText];
[[cell detailTopTextLabel] setText:[[gift givingTo] name]];
[[cell detailMiddleTextLabel] setText:[[gift reasonFor] name]];
[[cell detailBottomTextLabel] setText:[[gift boughtFrom] name]];
NSString *jpgName = [[NSString alloc] initWithFormat:#"Documents/%#.jpg", [gift imageKey]];
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:jpgName];
UIImage *imageToLoad = [[UIImage alloc] initWithContentsOfFile:jpgPath];
[[[cell image] layer] setBorderColor:[[UIColor lightGrayColor] CGColor]];
[[[cell image] layer] setBorderWidth:1];
[[cell image] setClipsToBounds:YES];
[[cell image] setContentMode:UIViewContentModeScaleAspectFill];
[[cell image] setImage:imageToLoad];
return cell;
}
I have created a custom cell using a xib file with a viewController named GTGiftCell.
There are two approaches you can take
If your application needs to load the images as they change then you keep with loading the images as you are , but you should read apples Concurrency Programming Guide to improve performance if the images need to update and change w/o the user refreshing the view
Load your images when the application loads but once again to improve performance read the Concurrency Programming Guide
Related
I am having an issue with tableview cells that are created from 2 different prototype cells. I add my own labels and images to them.
They load fine, but after i scroll in the tableview, and then select a cell, the labels from other cells are getting added to the labels already present in the cell.
I've read about similar issues people are having but none address the fact that it only occurs on selection.
I have tried adding if (cell == nil){}; but that has no effect.
My cellForRowAtIndexPath is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"NDSClassSubMenuViewController cellForRowAtIndexPath: running...");
NDSClassAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSDictionary *childNodes = [appDelegate.children objectAtIndex:0];
NSString *multicellstat = [childNodes objectForKey:#"#CELLTYPE"];
NSLog(#"Multicellstat %#", multicellstat);
NSLog(#"TESTING CHILD %#", childNodes);
//ONLY LOADING SUB MENU OPTIONS
NSString *cellType;
if (multicellstat == NULL){
NSLog(#"cellForRowAtIndexPath: #CellType == multicell. Making multicell.");
cellType = #"SegueCellSubmenu";
}else {
NSLog(#"cellForRowAtIndexPath: children variable is NOT NULL. Making MenuCell.");
cellType = #"SegueCellMulti";
}
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellType forIndexPath:indexPath];
if (multicellstat == NULL){
NSLog(#"Not adding picture.");
}else {
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
UIView *blackBG = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 60, 60)];
blackBG.backgroundColor = [UIColor grayColor];
NSURL *imageURL = [NSURL URLWithString:[childNodes objectForKey:#"#THUMBNAIL"]];
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:data];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 0, 60, 60);
int borderWidth = 1;
imageView.frame = CGRectMake(borderWidth, borderWidth, blackBG.frame.size.width-borderWidth*2, blackBG.frame.size.height-borderWidth*2);
dispatch_async(dispatch_get_main_queue(), ^{
[cell addSubview:blackBG];
[blackBG addSubview:imageView];
});
});
}
//NSDictionary *tweet = [[[[appDelegate.menuItems objectForKey:#"Table"] objectForKey:#"Parent"]objectAtIndex:indexPath.row] objectForKey:#"Child"];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(70, 10, 210, 30)];
UILabel *detailText = [[UILabel alloc] initWithFrame:CGRectMake(70, 35, 210, 30)];
[cell.contentView addSubview:label];
[cell.contentView addSubview:detailText];
NSString *text = [[appDelegate.children objectAtIndex:indexPath.row] objectForKey:#"#MENUDESC"];
NSString *name = [[appDelegate.children objectAtIndex:indexPath.row] objectForKey:#"#MENUDESC"];
//NSLog(#"cellForRowAtIndexPath MENU ITEMS %#", text);
//Title label
label.text = NULL;
label.text = text;
[label setFont: [UIFont fontWithName:#"Arial" size:16.0]];
//Detail label
detailText.text = NULL;
detailText.text = name;
[detailText setFont: [UIFont fontWithName:#"Arial" size:12.0]];
//detailText.textColor = [UIColor colorWithRed:186.0/255.0 green:186.0/255.0 blue:186.0/255.0 alpha:1];
detailText.textColor = [UIColor grayColor];
//cell.textLabel.text = text;
//cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
NSLog(#"Creating submenu item: %#", text);
NSLog(#"NDSClassSubMenuViewController: cellForRowAtIndexPath: finished.");
return cell;
}
Anyone know how I can solve this?
The key issue is that the code running synch should not assume that the cell it's modifying after the image request completes is the same cell (at the same indexPath) as when the image request began.
The correct recipe is like this:
use a cached result if we have one (so we don't do web requests every time we scroll), otherwise make an asynch request.
when the request completes, cache the result
use the original indexPath to determine if the cell is still visible. if it is, reload that indexPath. the newly cached result will now be available in cellForRowAtIndexPath.
Here's a snippet of code that describes how to correctly update at table after an asynch request. In your cellForRowAtIndexPath, check for a cached response before calling this:
// in cellForRowAtIndexPath:
NSString *urlString = [childNodes objectForKey:#"#THUMBNAIL"]
NSURL *imageURL = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (!cachedResponse) {
// we don't find this image/request in the cache, call asynch load
[self asynchLoad:urlString forIndexPath:indexPath];
} else {
// we got this image already, update the cell
UIImage *image = [UIImage imageWithData:cachedResponse.data];
// set the cell's UIImageView subview image to image
}
Here we load the image at urlString, cache the result, and then reload the indexPath after the load if it's still visible after the request completes:
- (void)asynchLoad:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath {
NSURL *imageURL = [NSURL URLWithString:[childNodes objectForKey:#"#THUMBNAIL"]];
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
// cache the response
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:self];
// important part - we make no assumption about the state of the table at this point
// find out if our original index path is visible, then update it, taking
// advantage of the cached image (and a bonus option row animation)
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
if ([visiblePaths containsObject:indexPath]) {
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationFade];
// because we cached the image, cellForRow... will see it and run fast
}
}
}];
}
I solved the problem by creating my own custom cell class and using storyboard to set the layout.
I recently started to learn objective c and I'm making an application based on tableView, imageview and scrollView.
In tableview you press a button and the application pushes to scrollview where there's an image inside imageview. I get a problem when I tried releasing a memory from imageview (the image I loaded when pressing the button). The problem is, that the image I previously loaded is still there.
Here's the code I've got:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *headTitle = [[listContent objectAtIndex:indexPath.row] objectForKey:kItemTitleKey];
if ([headTitle isEqualToString:#"Differentialekvationer"]){
leafViewController.title = headTitle;
[[self navigationController] pushViewController:leafViewController animated:YES];
NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:#"/c.png"];
UIImage *imageToLoad = [UIImage imageWithContentsOfFile:fullpath];
leafViewController.myImageView = [[UIImageView alloc] initWithImage:imageToLoad];
leafViewController.myScrollView = [[UIScrollView alloc] initWithFrame:leafViewController.view.bounds];
[leafViewController.myScrollView addSubview:leafViewController.myImageView];
leafViewController.myScrollView.contentSize = leafViewController.myImageView.bounds.size;
[leafViewController.view addSubview:leafViewController.myScrollView];
leafViewController.myScrollView.bounces = NO;
}else if ([headTitle isEqualToString:#"Gränsvärden"]){
leafViewController.title = headTitle;
[[self navigationController] pushViewController:leafViewController animated:YES];
NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:#"/e.png"];
UIImage *imageToLoad = [UIImage imageWithContentsOfFile:fullpath];
leafViewController.myImageView = [[UIImageView alloc] initWithImage:imageToLoad];
leafViewController.myScrollView = [[UIScrollView alloc] initWithFrame:leafViewController.view.bounds];
[leafViewController.myScrollView addSubview:leafViewController.myImageView];
leafViewController.myScrollView.contentSize = leafViewController.myImageView.bounds.size;
[leafViewController.view addSubview:leafViewController.myScrollView];
leafViewController.myScrollView.bounces = NO;
}
and i tried to free the memory basically with everyting
-(void)viewDidDisappear{
leafViewController.myImageView.image = nil;
leafViewController.myScrollView = nil;
leafViewController.view = nil;
[leafViewController.myImageView release];}
I don't know what to look for and where the problem lies. Help would be much appreciated :).
P.S. Please note that I've been learning objective c for 4 days now so I'm a newbie
I'm absolutely stumped on how to fix this issue.
So I have a UITableView and in the delegate method cellForRowAtIndex: I'm adding several subviews to each cell if the cell is nil (the initial building of the table view). Everything works well and the table view is built, however, when I scroll down a little in the application, the app all of a sudden crashes with SIGBART and gives me the error
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSIndexPath setImage:]: unrecognized selector sent to class 0x3c361e68'** It's weird because I'm not even calling a setImage method anywhere in my code.
Here is the code for the delegate method.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UIImageView* imageView;
UILabel* ttitle;
UILabel* ttitle2;
UILabel* ttitle3;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Configure cell:
// *** This section should configure the cell to a state independent of
// whatever row or section the cell is in, since it is only executed
// once when the cell is first created.
imageView=[[UIImageView alloc]initWithFrame:CGRectMake(10.0, 11.0, 50.0, 50.0)];
[imageView setContentMode:UIViewContentModeScaleAspectFill];
imageView.layer.masksToBounds=YES;
imageView.layer.cornerRadius=5.0;
[cell.contentView addSubview:imageView];
ttitle = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 7.0, 200, 20)] autorelease];
ttitle.textColor= [UIColor blackColor];
ttitle.numberOfLines=1;
ttitle.backgroundColor=[UIColor clearColor];
ttitle.font=[UIFont fontWithName:#"Arial Bold" size:15.0];
[cell.contentView addSubview:ttitle];
if (indexPath.row==0) {
CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:#"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.5, 200, size.height)] autorelease];
ttitle2.textColor= [UIColor darkGrayColor];
ttitle2.backgroundColor=[UIColor clearColor];
ttitle2.numberOfLines=0;
ttitle2.textAlignment = NSTextAlignmentLeft;
ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
ttitle2.font=[UIFont fontWithName:#"Arial" size:14.0];
[cell.contentView addSubview:ttitle2];
ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-8.0, 210, 40)] autorelease];
ttitle3.textColor= [UIColor darkGrayColor];
ttitle3.backgroundColor=[UIColor clearColor];
ttitle3.numberOfLines=1;
ttitle3.textAlignment = NSTextAlignmentLeft;
ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
ttitle3.font=[UIFont fontWithName:#"Arial" size:11.0];
[cell.contentView addSubview:ttitle3];
}
else{
CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:#"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.0, 200, size.height)] autorelease];
ttitle2.textColor= [UIColor darkGrayColor];
ttitle2.backgroundColor=[UIColor clearColor];
ttitle2.numberOfLines=0;
ttitle2.textAlignment = NSTextAlignmentLeft;
ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
ttitle2.font=[UIFont fontWithName:#"Arial" size:14.0];
[cell.contentView addSubview:ttitle2];
ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-9.0, 210, 40)] autorelease];
ttitle3.textColor= [UIColor darkGrayColor];
ttitle3.backgroundColor=[UIColor clearColor];
ttitle3.numberOfLines=1;
ttitle3.textAlignment = NSTextAlignmentLeft;
ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
ttitle3.font=[UIFont fontWithName:#"Arial" size:11.0];
[cell.contentView addSubview:ttitle3];
}
}
// Customize cell:
// *** This section should customize the cell depending on what row or section
// is passed in indexPath, since this is executed every time this delegate method
// is called.
imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:#"thumbnail"]]]];
[ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:#"name"]];
[ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:#"content"]];
NSString* first=[[[data objectAtIndex:indexPath.row] valueForKey:#"hashtag"] stringByAppendingString:#" "];
NSString* second =[first stringByAppendingString:[[data objectAtIndex:indexPath.row] valueForKey:#"place"]];
NSString* third=[second stringByAppendingString:#" "];
NSString* fourth=[third stringByAppendingString:#"¤ "];
NSString* conversion=[[[data objectAtIndex:indexPath.row] valueForKey:#"counter"] stringValue];
NSString* fifth=[fourth stringByAppendingString:conversion];
[ttitle3 setText:fifth];
return cell;
}
Appreciate the help guys!
*UPDATED CODE
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UIImageView* imageView;
UILabel* ttitle;
UILabel* ttitle2;
UILabel* ttitle3;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Configure cell:
// *** This section should configure the cell to a state independent of
// whatever row or section the cell is in, since it is only executed
// once when the cell is first created.
imageView=[[UIImageView alloc]initWithFrame:CGRectMake(10.0, 11.0, 50.0, 50.0)];
[imageView setContentMode:UIViewContentModeScaleAspectFill];
imageView.layer.masksToBounds=YES;
imageView.layer.cornerRadius=5.0;
imageView.tag=1;
[cell.contentView addSubview:imageView];
ttitle = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 7.0, 200, 20)] autorelease];
ttitle.textColor= [UIColor blackColor];
ttitle.numberOfLines=1;
ttitle.tag=69;
ttitle.backgroundColor=[UIColor clearColor];
ttitle.font=[UIFont fontWithName:#"Arial Bold" size:15.0];
[cell.contentView addSubview:ttitle];
if (indexPath.row==0) {
CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:#"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.5, 200, size.height)] autorelease];
ttitle2.textColor= [UIColor darkGrayColor];
ttitle2.backgroundColor=[UIColor clearColor];
ttitle2.numberOfLines=0;
ttitle2.tag=70;
ttitle2.textAlignment = NSTextAlignmentLeft;
ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
ttitle2.font=[UIFont fontWithName:#"Arial" size:14.0];
[cell.contentView addSubview:ttitle2];
ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-8.0, 210, 40)] autorelease];
ttitle3.textColor= [UIColor darkGrayColor];
ttitle3.backgroundColor=[UIColor clearColor];
ttitle3.numberOfLines=1;
ttitle3.tag=71;
ttitle3.textAlignment = NSTextAlignmentLeft;
ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
ttitle3.font=[UIFont fontWithName:#"Arial" size:11.0];
[cell.contentView addSubview:ttitle3];
}
else{
CGSize size=[[[data objectAtIndex:indexPath.row] valueForKey:#"content"] sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(265.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
ttitle2 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, 27.0, 200, size.height)] autorelease];
ttitle2.textColor= [UIColor darkGrayColor];
ttitle2.backgroundColor=[UIColor clearColor];
ttitle2.numberOfLines=0;
ttitle2.tag=70;
ttitle2.textAlignment = NSTextAlignmentLeft;
ttitle2.lineBreakMode=NSLineBreakByWordWrapping;
ttitle2.font=[UIFont fontWithName:#"Arial" size:14.0];
[cell.contentView addSubview:ttitle2];
ttitle3 = [[[UILabel alloc] initWithFrame:CGRectMake(70.0, ttitle2.frame.origin.y+ttitle2.frame.size.height-9.0, 210, 40)] autorelease];
ttitle3.textColor= [UIColor darkGrayColor];
ttitle3.backgroundColor=[UIColor clearColor];
ttitle3.numberOfLines=1;
ttitle3.tag=71;
ttitle3.textAlignment = NSTextAlignmentLeft;
ttitle3.lineBreakMode=NSLineBreakByWordWrapping;
ttitle3.font=[UIFont fontWithName:#"Arial" size:11.0];
[cell.contentView addSubview:ttitle3];
}
imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:#"thumbnail"]]]];
[ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:#"name"]];
[ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:#"content"]];
NSString* first=[[[data objectAtIndex:indexPath.row] valueForKey:#"hashtag"] stringByAppendingString:#" "];
NSString* second =[first stringByAppendingString:[[data objectAtIndex:indexPath.row] valueForKey:#"place"]];
NSString* third=[second stringByAppendingString:#" "];
NSString* fourth=[third stringByAppendingString:#"¤ "];
NSString* conversion=[[[data objectAtIndex:indexPath.row] valueForKey:#"counter"] stringValue];
NSString* fifth=[fourth stringByAppendingString:conversion];
[ttitle3 setText:fifth];
}
else {
imageView =[cell viewWithTag:1];
ttitle=[cell viewWithTag:69];
ttitle2=[cell viewWithTag:70];
ttitle3=[cell viewWithTag:71];
}
//STUFFOUTSIDE
// Customize cell:
// *** This section should customize the cell depending on what row or section
// is passed in indexPath, since this is executed every time this delegate method
// is called.
return cell;
}
The problem is that your local variables are not being initialized when the cell is re-used. Here's the current flow for imageView:
UIImageView* imageView;
if (cell == nil)
{
// Create imageView
imageView=...
}
// If cell is being reused (ie cell is not nil) then imageView is nil at this point.
imageView.image=...
When you are reusing a table view cell, tableView:dequeueReusableCellWithIdentifier: returns an actual cell instead of nil and the initialization of imageView is skipped.
You need to "find" the imageView that is in the reused cell in order to make changes to it.
Therefore, I suggest that you modify your logic like this:
UIImageView* imageView;
if (cell == nil)
{
// Create imageView
imageView=...
}
else
{
imageView = // get a reference to the imageView
}
imageView.image=...
So now, of course, the question is "how?".
A very common way is to set the tag of the view when you create it so that you can easily retrieve it at a later time. You would use this technique like this:
// Use a unique tag number for each subview.
#define MY_IMAGEVIEW_TAG 1000
UIImageView* imageView;
if (cell == nil)
{
// Create imageView
imageView=... // Same as before
imageView.tag = MY_IMAGEVIEW_TAG;
}
else
{
// This is a cell that is being re-used and was previously created.
// Retrieve a reference to the existing image view that is already in the cell
imageView = [cell viewWithTag:MY_IMAGEVIEW_TAG];
}
// Now imageView is "safe" to use whether it is a new cell or one that is reused!
imageView.image=...
NOTE: If you are doing a lot of this, creating a UITableViewCell subclass that has properties for each of these subviews would make the use of tags and viewWithTag unnecessary, as well as make your code easier to read.
#Inafziger has already posted the correct answer to this question, I just want to explain a little bit more in detail why you're seeing this "weird" crash.
I wouldn't recommend the excessive usage of tags, though. It might be a better idea to create a subclass of UITableViewCell.
You're not initializing your imageView and ttitle variables:
UIImageView *imageView; // imageView can point to anything now!
UILabel* ttitle;
Usually, you'd initialize local variables to nil or 0 (whatever makes sense) when you declare them to avoid such dangling pointers.
Because you're reusing your table view cells cell won't always be nil and your if-clause won't be executed:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) { // Won't be executed if the cell could be dequeued!
...
imageView = ...;
}
Hence, if cell can be dequeued, your imageView and ttitle variables have still not been assigned to anything when you use them!
You're then setting the attributes of the views:
imageView.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[data objectAtIndex:indexPath.row] valueForKey:#"thumbnail"]]]];
[ttitle setText:[[data objectAtIndex:indexPath.row] valueForKey:#"name"]];
[ttitle2 setText:[[data objectAtIndex:indexPath.row] valueForKey:#"content"]]
imageView.image = ...; is the same as calling [imageView setImage:...];. You can read more about that here: http://www.cocoacast.com/cocoacast/?q=node/103
And that's when all comes together: imageView isn't initialized and you're calling -setImage: on it. Boom, crash!
In your case imageView points to the NSIndexPath class. This could be anything, though.
Because of that you're effectively calling -setImage: on the NSIndexPath class (equivalent to: +[NSIndexPath setImage:]) and the app crashes with the +[NSIndexPath setImage:]: unrecognized selector sent to class 0x3c361e68 error message.
In my application (code listed below), I use a popover to display a series of colors that the user can choose. These colors are used for the color of the drawing they are completing above. I am trying to modify the popover to work the same way, except for this time I would want to display images (the images are saved in the application's documents folder as png files) instead of blocks of color. Listed below is the working code for the color selector popover. ColorGrid is a UIview which contains an NSArray Colors, as well as two NSUIntegers columnCount and rowCount. I have tried to replace the items in the colors array with UIImages of the png files, as well as UIImageViews but I have not been able to get a successful result (or a compilable one). Listed below is the working code. Could anyone show me how I can change the UIColor items to the images to show them in the grid?
- (IBAction)popoverStrokeColor:(id)sender {
StrokeColorController *scc = [[[StrokeColorController alloc] initWithNibName:#"SelectColorController" bundle:nil] autorelease];
scc.selectedColor = self.strokeColor;
[self doPopoverSelectColorController:scc sender:sender];
}
- (void)doPopoverSelectColorController:(SelectColorController*)scc sender:(id)sender {
[self setupNewPopoverControllerForViewController:scc];
scc.container = self.currentPopover;
self.currentPopover.popoverContentSize = scc.view.frame.size;
scc.colorGrid.columnCount = 2;
scc.colorGrid.rowCount = 3;
scc.colorGrid.colors = [NSArray arrayWithObjects:
//put the following below back in after testing
[UIColor blackColor],
[UIColor blueColor],
[UIColor redColor],
[UIColor greenColor],
[UIColor yellowColor],
[UIColor orangeColor],
//[UIColor purpleColor],
// [UIColor brownColor],
// [UIColor whiteColor],
// [UIColor lightGrayColor],
//[UIColor cyanColor],
//[UIColor magentaColor],
nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(colorSelectionDone:) name:ColorSelectionDone object:scc];
[self.currentPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; //displays the popover and anchors it to the button
}
Thanks for your help. I am new to objective-c.
edit - heres the function with my attempt to insert the images instead of the colors
- (void)doPopoverSelectColorController:(SelectColorController*)scc sender:(id)sender {
[self setupNewPopoverControllerForViewController:scc];
scc.container = self.currentPopover;
self.currentPopover.popoverContentSize = scc.view.frame.size;
// these have to be set after the view is already loaded (which happened
// a couple of lines ago, thanks to scc.view...
scc.colorGrid.columnCount = 2;
scc.colorGrid.rowCount = 3;
//here we need to get the UIImage items to try to put in the array.
NSArray *pathforsave = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [pathforsave objectAtIndex:0];
//here we need to add the file extension onto the file name before we add the name to the path
//[fileName appendString:#".hmat"];
NSString *strFile = [documentDirectory stringByAppendingPathComponent:#"test.png"];
NSString *strFile1 = [documentDirectory stringByAppendingPathComponent:#"test1.png"];
NSString *strFile2 = [documentDirectory stringByAppendingPathComponent:#"test2.png"];
NSString *strFile3 = [documentDirectory stringByAppendingPathComponent:#"test3.png"];
NSString *strFile4 = [documentDirectory stringByAppendingPathComponent:#"test4.png"];
NSString *strFile5 = [documentDirectory stringByAppendingPathComponent:#"test5.png"];
//now for the Images
UIImage *image = [ UIImage imageWithContentsOfFile: strFile];
UIImage *image1 = [ UIImage imageWithContentsOfFile: strFile1];
UIImage *image2 = [ UIImage imageWithContentsOfFile: strFile2];
UIImage *image3 = [ UIImage imageWithContentsOfFile: strFile3];
UIImage *image4 = [ UIImage imageWithContentsOfFile: strFile4];
UIImage *image5 = [ UIImage imageWithContentsOfFile: strFile5];
UIImageView *imageview = [[[UIImageView alloc] initWithImage:image] autorelease];
[self.view addSubview:imageview];
UIImageView *imageview1 = [[[UIImageView alloc] initWithImage:image1] autorelease];
[self.view addSubview:imageview1];
UIImageView *imageview2 = [[[UIImageView alloc] initWithImage:image2] autorelease];
[self.view addSubview:imageview2];
UIImageView *imageview3 = [[[UIImageView alloc] initWithImage:image3] autorelease];
[self.view addSubview:imageview3];
UIImageView *imageview4 = [[[UIImageView alloc] initWithImage:image4] autorelease];
[self.view addSubview:imageview4];
UIImageView *imageview5 = [[[UIImageView alloc] initWithImage:image5] autorelease];
[self.view addSubview:imageview5];
imageview.image = image;
imageview1.image = image1;
imageview2.image = image2;
imageview3.image = image3;
imageview4.image = image4;
imageview5.image = image5;
scc.colorGrid.colors = [NSArray arrayWithObjects:
// When attempting to add the images like this - get the error identified expected
// after the e in image, at the end bracket. Putting a * does nothing to change the error
[image],
// When adding one of the Imageviews, i get the same error as above
//below is how I attempted to add it
[imageView],
//
nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(colorSelectionDone:) name:ColorSelectionDone object:scc];
[self.currentPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; //displays the popover and anchors it to the button
}
Remove your square brackets around image and/or imageView :
scc.colorGrid.colors = [NSArray arrayWithObjects:
// Not : [image] but
image,
// or
imageView,
// Not : [imageView],
nil];
I use EGOPhotoView for my image local gallery. The number of images is 152 and their resolution is 1400x950px.
I have problems with the memory of my device! Memory does not get released. I display one image 45mb + next 55mb + next 72mb.....and over 130mb the app crashes.
In this code I have added images:
NSMutableArray *photos = [[NSMutableArray alloc] init];
for (Picture *picture in [self fetchedResultsController].fetchedObjects) {
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:#"%#.jpg", picture.imgName]];
MyPhoto *photo = [[MyPhoto alloc] initWithImageURL:nil name:[NSString stringWithFormat:#"%#, %#, %#", picture.friendlyName, picture.type, picture.date] image:img painter:(Painter *)picture.painter];
[photos addObject:photo];
[photo release];
}
MyPhotoSource *source = [[MyPhotoSource alloc] initWithPhotos:[NSArray arrayWithArray:photos]];
EGOPhotoViewController *photoController = [[EGOPhotoViewController alloc] initWithPhotoSource:source];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:photoController];
navController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
navController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController:navController animated:YES];
[navController release];
[photoController release];
[source release];
[photos release];
I use EGOPhotoViewer as a modalView.
What could be the problem I'm having?
Even though you're explicitly releasing the instances of UIImage, the photos array that you're using still has a reference to them. This means that the images aren't being released from memory as it appears you're expecting.
I'm not familiar with EGOPhotoViewer, unfortunately. Is there a way you can lazy load the images? There are probably other options such as reducing the size of the images, but that's dependent upon what your requirements are.