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

Updating a cell with less-important data #606

Closed iostriz closed 7 years ago

iostriz commented 7 years ago

This is a question about best practice on updating a cell on a less important data. Let's pretend we have a quite heavy model: couple of images, some large data and so on, and one minor info counter, that counts something. Simplified:

@interface RecipientModel : NSObject <IGListDiffable>

@property (nonatomic, readonly) NSString *compositeName;
@property (nonatomic, readonly) UIImage *profileImage;
// ... striped for brevity
@property (nonatomic) NSUInteger msgCount;  // can be set from outside, that's why it is readwrite

+ (instancetype)__unavailable new;
- (instancetype)__unavailable init;

//... striped

@end

I want to update msgCount on some events (that happens on e.g. didSelectItemAtIndex: and/or some external notification). My instincts tell me that I should not invalidate complete model instance (because loading other data, images and stuff may be expensive). I would prefer updating this model (and cell) with this irrelevant info and avoid all heavy machinery. Also, this counter should not be part of the diff-ing mechanism.

What is the preferred way to do this? Currently I'm using [_adapter reloadObjects:@[viewModel]]; via delegation from section controller to view controller - is it expensive using it like this? Is there a better way?

Sorry for wasting your time if it is a dummy question.

New issue checklist

General information

rnystrom commented 7 years ago

Not a dumb question! This gets into some fun architecture stuff. I could geek out on this stuff all day 🤓

In this case, I would create an object who's responsibility is announcing certain changes, hand that object to each section controller that cares (via init, aka dependency injection), and add the section controller as a listener for changes.

Whether you do this via NSNotificationCenter, KVO, a custom one-to-many delegate, or w/e is totally up to you. But this way you are sending scoped events (e.g. not global) to each section controller.

This is exactly how we handle this situation in Instagram. Great question 😄

iostriz commented 7 years ago

Yeah, thx. I'll do as you've suggested. I'll inject counter watcher and (in this case) observe property change. Btw. I'm getting collection view 'NSInternalInconsistencyException', reason: 'attempt to delete section 207, but there are only 0 sections before the update', and I'm not using sections at all (with the latest HEAD e9e09d), that I did not experience before... Still investigating, but have to ask if it is maybe experienced on your side too? If (when) I get more info, I'll get back... Closing till then.

rnystrom commented 7 years ago

@iostriz Hmm haven't seen anything like that on our end. If you continue to hit it would you mind opening a new issue and maybe a sample project so we can track?

iostriz commented 7 years ago

Hmmm... Now I have a concrete question that goes with the title. So, instead of opening a new issue, I'm reopening this one. Is it possible to modify the cell appearance (sectionController) from other places but - (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index?

For example, this works:

- (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index
{
    RecipientsCell *cell = [self.collectionContext dequeueReusableCellWithNibName:@"RecipientsCell" bundle:nil forSectionController:self atIndex:index];
    cell.model = _model.contact;
    [cell setInfo:@"aaa"];
    return cell;
}

but this I can't get to work:

- (void)listAdapter:(IGListAdapter *)listAdapter willDisplaySectionController:(IGListSectionController<IGListSectionType> *)sectionController cell:(UICollectionViewCell *)cell atIndex:(NSInteger)index
{
    RecipientsCell *c = (RecipientsCell *)cell;
    [c setInfo:@"42"];
}

// or this 

- (void)listAdapter:(IGListAdapter *)listAdapter didEndDisplayingSectionController:(IGListSectionController<IGListSectionType> *)sectionController cell:(UICollectionViewCell *)cell atIndex:(NSInteger)index
{
    RecipientsCell *c = (RecipientsCell *)cell;
    [c setInfo:@"4242"];    
}

Is this intentional?

jessesquires commented 7 years ago

@iostriz if you're still seeing this, let's open a new issue

cellForItemAtIndex: is the correct place to configure cells

iostriz commented 7 years ago

Thanks Jesse.