4
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ Have you taken a look at the approaches existing tools like github.com/rs/SDWebImage have used? \$\endgroup\$ – Jonah Jun 22 '16 at 21:10
1
\$\begingroup\$

I have a possible solution for your case, this is what I am thinking. 1) You can implement scrollViewDidEndDragging delegate and load your image for the visible cells. for ex.

  - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
            [self loadImagesForOnScreenRows];
    }

loadIamgesForOnScreenRows will look like:

- (void)loadImagesForOnScreenRows
{

    NSArray *visiblePaths = [self.tracksListing indexPathsForVisibleRows];

    for (NSIndexPath *indexPath in visiblePaths){
            [[self.tableView cellForRowAtIndexPath:indexPath] bindItemObject:record];
        }
    }
}

In my case bindItemObject is for downloading the image implemented in tableViewCell class, and record contains the url for the image.

The only problem which you will encounter in this case is, when the tableView appears for the first time. In that case I suggest you to programmatically call the

[self.tableView scrollToRowAtIndexPath:indexPath
                          atScrollPosition:UITableViewScrollPositionTop
                                  animated:YES]; 

and give indexpath as indexpath for row:0 and section:0

I think this can solve your problem.

\$\endgroup\$
  • \$\begingroup\$ You don't need the scrollToRowAtIndexPath bit Just call [self loadImagesForOnScreenRows]; manually \$\endgroup\$ – trapper May 8 '18 at 2:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.