I'm trying to come up with a system to populate my item images on demand, this is what I have so far. One obvious bug is that an item image could be downloaded multiple time since the _image
will remain nil until the first download has completed. I could work around this with a loading property.
My main issue is about priority, ideally I would like the images currently visible in the tableview to be downloaded first. ie As soon as the tableview appears it starts downloading the first 20 or so initially visible item.images, but if the users scrolls down to the bottom then items 20-100 all get queued in order which results in the now visible images 80-100 being last to download. Not really sure how to deal with this.
@interface Item : NSObject
@property (nonatomic, strong) UIImage *image;
@end;
@implementation Item
- (UIImage *)image
{
if (!_image) {
NSURL *url = [NSURL URLWithString:@"http://loremflickr.com/50/50/paris"];
NSURLSessionDataTask *imageTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
_image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"ItemUpdated" object:self];
});
}];
[imageTask resume];
}
return _image;
}
@end
@interface TableViewController : UITableViewController
@property (nonatomic, strong) NSArray *items;
@end
@implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:100];
for (int i=0; i<100; i++) {
Item *item = [[Item alloc] init];
[items addObject:item];
}
self.items = items;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemUpdated:) name:@"ItemUpdated" object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemUpdated" object:nil];
}
- (void)itemUpdated:(NSNotification *)notification
{
Item *item = (Item *)notification.object;
NSUInteger index = [self.items indexOfObject:item];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Item *item = [self.items objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = item.image;
cell.textLabel.text = [NSString stringWithFormat:@"Item %d", indexPath.row];
return cell;
}
@end