2

I'm loading pictures into a table view that correspond to the cell text, so each image is different. As the user scrolls down, iOS has to manually reload each image from the SSD, so scrolling is very choppy. How do I cache images or prevent the table view cells from needing to be recreated? Others have had their issues solved by using imageNamed: as iOS will automatically cache your images, but I am loading images from the documents directory, not the app bundle. What should I do? Thanks.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    // Return the number of sections.
    return 1;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    // Return the number of rows in the section.
    return [issues count];

}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];

    }


    // Set up the cell...
    NSDictionary *dic = [self.issues objectAtIndex:indexPath.row];

    cell.text = [dic objectForKey:@"Date"];

    cell.imageView.image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/issues/%@/cover.png", documentsDirectory, [dic objectForKey:@"Directory Name"]]];

    return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

        return 150;

}
Jack Humphries
  • 13,056
  • 14
  • 84
  • 125
  • Checkout UIImageLoader. It does exactly what you want and is only two files to add to xcode. It's also super easy to understand and there's two great samples in the repo. https://github.com/gngrwzrd/UIImageLoader – gngrwzrd Dec 25 '15 at 20:36

2 Answers2

4

That array of dictionaries is your model, so it would be a natural place. The tricky part is making your model mutable. You can either make your model mutable in the class, or jump through the hoops when you cache the image. I'd recommend the former, but coded it here to match your existing code...

- (UIImage *)imageForIndexPath:(NSIndexPath *)indexPath {

    NSDictionary *dic = [self.issues objectAtIndex:indexPath.row];
    UIImage *image = [dic valueForKey:@"cached_image"];

    if (!image) {
        image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/issues/%@/cover.png", documentsDirectory, [dic objectForKey:@"Directory Name"]]];
        NSMutableDictionary *updatedDictionary = [NSMutableDictionary dictionaryWithDictionary:dic];
        [updatedDictionary setValue:image forKey:@"cached_image"];
        NSMutableArray *updatedIssues = [NSMutableArray arrayWithArray:self.issues];
        [updatedIssues replaceObjectAtIndex:indexPath.row withObject:[NSDictionary dictionaryWithDictionary:updatedDictionary]];
        self.issues = [NSArray arrayWithArray:updatedIssues];
    }
    return image;
}

This will be choppy on the first scroll through the list, then smoother thereafter. If you'd like to have no first-time chop and incur a little latency before the view appears, you can walk your model ahead of time and force the load:

for (int i=0; i<self.issues.count; i++) {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
    (void)[self imageForIndexPath:indexPath];
}

One last thing - Another solution is a mutable array to match your issues array. That may be just fine, but more often than not, I end up glad that I included some cached calculation with my model. If you find yourself using that issues array elsewhere, you'll be happy that you have the images all taken care of.

danh
  • 62,181
  • 10
  • 95
  • 136
  • Thanks, I'll try that as soon as I can! Is there anything else I have to do besides copying and pasting the code in? Sorry, I don't have a lot of experience with UITableViews :) – Jack Humphries Mar 23 '12 at 14:35
  • Not that I can think of. It should work as a first step. The next step is looking at making your model mutable, this will simplify and speed up the work in the image cacheing method. – danh Mar 23 '12 at 14:39
  • @danh self.issues = [NSArray arrayWithArray:self.issues]; must be self.issues = [NSArray arrayWithArray:updatedIssues]; right? – OzBoz Feb 22 '13 at 12:03
  • @OzBoz you're right. thanks. reading back, not sure why I was focussing on the mutating the model. at the time, i thought that was preventing the OP from using the model as an image cache, but now I don't see evidence that that was the problem (even in an edit of the question). maybe it was an early edit? good catch, anyway. will fix my answer. – danh Feb 22 '13 at 15:54
1

Along with caching you may also consider loading the images in background using Grand central dispatch. When the cell is loaded put a UIActivityIndicator then replace it with an image in a separate thread.

Also checkout this related answer for image stutter: Non-lazy image loading in iOS

Community
  • 1
  • 1
amleszk
  • 6,192
  • 5
  • 38
  • 43
  • 1
    Thanks a lot. I am looking at Grand Central Dispatch and found a good sample project here (https://github.com/SlaunchaMan/GCDExample) – Jack Humphries Mar 23 '12 at 23:04