Table Views and Visible Cells

Discussion in 'iOS Programming' started by Soulstorm, Sep 8, 2009.

  1. Soulstorm macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #1
    I have a table view, which has 2 labels and an image view. The image view will display images of 4kBytes in size.

    However, when I add 20-30 cells, the iPhone struggles at scrolling, since it tries to rearrange the cells as they are moved upwards or downwards.

    Is there any way to display the images in the table cells only in the cells that are currently displayed on screen? I know that there is a function on the tableview named "visibleCells", but I can't find a decent logic to implement.

    Can anyone help me?
     
  2. Troglodyte macrumors member

    Joined:
    Jul 2, 2009
    #2
    As far as I know the tableview is already doing what you suggest. The problem is probably to do with compositing and it might be better to pre-render the cell contents into a single view rather than make the tableview do all the work every time the table view is moved. There is an example on the dev site of how to do this.
     
  3. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #3
    As Troglodyte said, normally the tableViewController is already doing this for you. Unless you've somehow managed to bypass some of the efficiency coding of cellForRowAtIndexPath: that deals with dequeueReusableCellWithIdentifier: and reuseIdentifier:.
     
  4. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #4
    This is my code.

    Code:
     static NSString *CellIdentifier;
    
    UITableViewCell *cell;
    
    if (self.imageThumbnailsEnabledInMainView == YES) {
         CellIdentifier = @"ImageSubtitleCell";
         cell = (ImageSubtitleCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
         if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@"ImageSubtitleCell"  owner:selfoptions:nil];
              cell = [nib objectAtIndex:0];
         [self.activeCells addObject:cell];
    
         }
    }else {
         CellIdentifier = @"Cell";
         cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
         if (cell == nil) {
              cell = [[[UITableViewCell alloc ]initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:CellIdentifier]autorelease];
    //[self.activeCells addObject:cell];
         }
    }
    
    // Set up the cell...
    NSManagedObject *managedObject;
    if (tableView == searchDisplayController.searchResultsTableView) {
         managedObject = [self.filteredManagedOjects objectAtIndex:indexPath.row];
         cell.textLabel.textColor = [UIColor blackColor];
    }
    else{
         managedObject = [self.imagesArray objectAtIndex:indexPath.row];
         cell.textLabel.textColor = [UIColor whiteColor];
    }
    
    cell.textLabel.text = @"a Text";
    cell.imageView.image = [self loadAppropriateImageForItem:[self.items objectAtIndex:indexpath.row"]];
    cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
    
    cell.autoresizesSubviews = YES;
    
    cell.detailTextLabel.textColor = [UIColor lightGrayColor];
    cell.detailTextLabel.text = @"secondaryText"
    return cell;
    
    Although I have fixed the problem with the slugginess (I had forgotten to set the "identifier" property in the NIB file) I have a new question:

    As you can see, all images are loaded as the user scrolls up and down and the thumbnails are displayed. Is there any way I can deallocate the images inside the cells that are not displayed so that the memory allocated for the images can be kept low?

    EDIT:

    Problem not fixed. When scrolling up and down, the application continues to allocate memory. Help!
     
  5. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    Perhaps. But is this really what you'd want? I think you need to make sure you strike a proper balance between memory footprint and image-loading performance. You don't want your scrolling to become sluggish again because you are having to reload all the images you deallocated. And since your images are pretty small in size (4K), I would think you'd rather keep them cached and taking up memory than uncaching them and burning processor to reload them constantly.
     
  6. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #6
    Thanks for the advice but...

    1) Actually, as you can read in the edit... The problem was not fixed. When scrolling up and down, the application continues to allocate memory. Any ideas as to why this may happen?
    2) Caching images is a good idea indeed, but what happens if the user has 150 images? Wouldn't this cause a slowdown?
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    That's not the way to do it. You should implement didReceiveMemoryWarning and release the images there. You don't show your code that loads the images. In fact if the image you assign to the cell is autoreleased then the only ones that will exist will be the ones visible, plus a few more.

    In general you should use the memory available but clean things up in response to a memory warning.
     
  8. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #8
    I see. However, I would really like to know why the following happens: Here is my code:

    in the ViewDidLoad I load the images array, and I cache the images
    Code:
    for (NSManagedObject *obj in self.imagesArray) {
    		NSString *str =[obj valueForKey:@"filePath"];
    		
    		[self.cachedImages addObject:[UIImage imageWithContentsOfFile:str]];
    	}
    
    and this is my cellForRowAtIndexPath code:

    Code:
    
    UITableViewCell *cell;	
    if (self.imageThumbnailsEnabledInMainView == YES) {
    	CellIdentifier = @"ImageSubtitleCell";
    	cell = (ImageSubtitleCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    	if (cell == nil) {
    		NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@"ImageSubtitleCell" owner:self options:nil];
    		cell = [nib objectAtIndex:0];
    	}
    }else {
    	CellIdentifier = @"Cell";
    	cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    	if (cell == nil) {
    		cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]autorelease];
    	}
    }
    
    NSManagedObject *managedObject;
    if (tableView == searchDisplayController.searchResultsTableView) {
    	managedObject = [self.filteredManagedOjects objectAtIndex:indexPath.row];
    	cell.textLabel.textColor = [UIColor blackColor];
    }
    else{
    	managedObject = [self.imagesArray objectAtIndex:indexPath.row];
    	cell.textLabel.textColor = [UIColor whiteColor];
    }
    	
    cell.textLabel.text = [NSString stringWithFormat:@"%i : %@", indexPath.row, [managedObject valueForKey:@"imageName"]];
    	
    if (self.imageThumbnailsEnabledInMainView == YES) {
    	cell.imageView.image = [self.cachedImages objectAtIndex:indexPath.row];
    	cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    	
    cell.autoresizesSubviews = YES;
    	
    cell.detailTextLabel.textColor = [UIColor lightGrayColor];
    cell.detailTextLabel.text = [managedObject valueForKey:@"date"];
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    	
    
    By running Instruments, I noticed that as I scroll up and down, the application is allocating more memory, even if all the cells have been shown at least once on the screen.

    I know I am missing something simple here, I just can't find it!

    EDIT: Most strange thing is that this happens on the iPhone, but not on the Simulator!
     
  9. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #9
    Allocating more memory to what? And where?
     
  10. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #10
    If you run the application with Instruments, and you see the "ObjectAlloc" instrument, you can see the memory consumption from objects allocated within the application.

    You can also see the total RAM usage in the "CPU Monitor" instrument.

    Total RAM usage is boosted up each time I scroll up and down the table view.

    The strange thing is that the same thing happens with Apple's own sample code in TableViewSuite.
     
  11. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #11
  12. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #12
    Thanks for the info. However, I have also checked my app with Activity monitor, so I insist that something is wrong with my code.
     
  13. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #13
    OK, something's wrong with your code. ObjectAlloc has a complete record of every object created by your app. What objects are piling up and not being released? Have you run the leaks tool?
     
  14. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #14
    Yeah, that's the question I was kind of alluding to in my post #9.
     
  15. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #15
    You could define a delegate object for your custom table cell. The cell could call out to the delegate when it needs an image to display. You can keep the images cached in your delegate and just draw the image with drawAtPoint: in a subview.

    I noticed this line:

    Code:
    cell.textLabel.text = [NSString stringWithFormat:@"%i : %@", indexPath.row, [managedObject valueForKey:@"imageName"]];
    Try caching an array of strings instead of creating them each time and see if that helps with the allocations.
     
  16. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #16
    PhoneyDeveloper OK, now I see how things work. "Leaks" does not show a memory leak, and my application's memory footprint is reduced greatly if I leave my tableview alone, after many seconds. So, I think that my problem is no more.

    Thank you for your answers.

    BruinEcon08, that's a very good advice, thank you. I hadn't thought about that.
     
  17. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #17
    Code:
    cell.imageView.contentMode = UIViewContentModeScaleAspectFill
    Also, from my own personal experience, you *really* want your content to be pre-rendered to the appropriate scale. The performance hit from having the OS scale each image in each cell while scrolling was considerable with one of my old projects.
     

Share This Page