Instagram / IGListKit

A data-driven UICollectionView framework for building fast and flexible lists.
https://instagram.github.io/IGListKit/
MIT License
12.87k stars 1.54k forks source link

reloadInSectionController:atIndexes: not reloading #518

Closed ghost closed 7 years ago

ghost commented 7 years ago

It seams that IGListCollectionContext reloadInSectionController:atIndexes: is not reloading. I am using IGListCollectionView to show images and additional text information as overlays on the cells. I wrote a custom image loader and cache as various different caching methods/times/sources/destinations are involved.

When the cell is loaded, it checks if the required image is contained in the cache (NSCache) of the section controller or if the image has to be loaded. If the image is not already in the cache a custom imageLoader is used to load the image from the required location (hard disk or web) and the image data is delivered via delegate to the section controller. The delegate function is triggered and the image is added to the cache but reloadInSectionController:atIndexes: does not have any effect.

The section controller holds a collection of items, as I need a grid layout.

When scrolling up and down again, the cells and the images are loaded correctly.

Sample (Section Controller):

// image cache
@property (strong, nonatomic) NSCache *imageCache;

...

// cell for index
- (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index {
    ...
    // load image data from cache or load data from source
    NSData *imageData = [_imageCache objectForKey:key];
    if (imageData) {
        [[cell imageView] setImage:[UIImage imageWithData:imageData]];
    }
    else {
        [_imageLoader getImageDataForKey:key withIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
    }
}

...

// delegate function
- (void)loadedImageData:(NSData*)imageData forKey:(NSString*)key withIndexPath:(NSIndexPath*)indexPath {
    if (imageData) {
        [_imageCache setObject:imageData forKey:key];
        [self.collectionContext reloadInSectionController:self atIndexes:[NSIndexSet indexSetWithIndex:indexPath.row]];
    }
}

New issue checklist

General information

rnystrom commented 7 years ago

@furry-coding what are the internals of your image loader? I see you're passing [NSIndexPath indexPathForRow:index inSection:0] to it. Does the image loader use the section at all? If it does, I wonder if its sending delegate events to the wrong section controller.

You can get the current section with:

NSInteger section = [self.collectionContext sectionForSectionController:self];

Some debugging tips to try:

Put a break at your reloadInSectionController: call. Once you hit that, put a break in cellForItemAtIndex:. Is that method being called?

Put a break where you call getImageDataForKey: on the image loader that does this:

screen shot 2017-02-28 at 11 17 15 am

Put another break in your delegate method that does the same thing. Are the addresses for the section controllers matching? e.g. if the image loader is called for <IGListSectionController: 0x1234> you should see the delegate method print the same instance/address.

Let us know what you find!

ghost commented 7 years ago

@rnystrom Thank you for the quick reply.

The section is not used in this case. The image loader is universal and can be used as well by UICollectionView/UITableView/etc. where the section has an effect.

In this case the delegate is send to the correct section controller. The call

[_imageCache setObject:imageData forKey:key];

adds the image data to the cache of this very section controller. But the cellForRowAtIndex: is not called. Thus, I supposed the reloadInSectionController:atIndexes: methods doesn't work properly.

rnystrom commented 7 years ago

@furry-coding can you put a break inside this method to see what path things are taking? You can also put breaks inside the IGListAdapterUpdater methods to see if they are being called.

ghost commented 7 years ago

@rnystrom the method mentioned above is not called at all. When using reloadSectionController: instead of reloadInSectionController:atIndexes: the images are presented correctly, but the whole section controller is updated for every image, which isn't an option.

I found out, that everything works fine, if the images are loaded with some latency (e.g. from the internet). If the images are loaded from the local storage, the delegate returns before the cells are actually presented (no matter if I call reloadDataWithCompletion: or performUpdatesAnimated:completion: before).

rnystrom commented 7 years ago

@furry-coding ahhhhhh, interesting. That makes sense then. The cells haven't been created/inserted into the view yet. Some ideas:

Also it'd be worth making your API synchronous if you hit the cache so you can immediately return the image data if you want to keep using the delegate API. I'd still say go w/ blocks for this.

ghost commented 7 years ago

@rnystrom ok thanks for the ideas. I'll give it a shot.