Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider supplementary views when sending display events #470

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ This release closes the [3.0.0 milestone](https://github.com/Instagram/IGListKit

- Empty Views now move with Refresh Controls, and no longer use the `_collectionView.backgroundView` property. [dshahidehpour](https://github.com/dshahidehpour) [(#462)](https://github.com/Instagram/IGListKit/pull/462)]

### Fixes

- Consider supplementary views with display and end-display events. [Ryan Nystrom](https://github.com/rnystrom) [(#470)](https://github.com/Instagram/IGListKit/pull/470)


### Enhancements

Expand Down
68 changes: 51 additions & 17 deletions Source/IGListAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#import "IGListSectionControllerInternal.h"

@implementation IGListAdapter {
NSMapTable<UICollectionViewCell *, IGListSectionController<IGListSectionType> *> *_cellSectionControllerMap;
NSMapTable<UICollectionReusableView *, IGListSectionController<IGListSectionType> *> *_viewSectionControllerMap;
BOOL _isDequeuingCell;
BOOL _isSendingWorkingRangeDisplayUpdates;
}
Expand Down Expand Up @@ -49,7 +49,7 @@ - (instancetype)initWithUpdater:(id <IGListUpdatingDelegate>)updater
_displayHandler = [[IGListDisplayHandler alloc] init];
_workingRangeHandler = [[IGListWorkingRangeHandler alloc] initWithWorkingRangeSize:workingRangeSize];

_cellSectionControllerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality | NSMapTableStrongMemory
_viewSectionControllerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality | NSMapTableStrongMemory
valueOptions:NSMapTableStrongMemory];

_updater = updater;
Expand Down Expand Up @@ -396,7 +396,7 @@ - (NSArray *)objects {
NSArray<UICollectionViewCell *> *visibleCells = [self.collectionView visibleCells];
NSMutableSet *visibleSectionControllers = [NSMutableSet new];
for (UICollectionViewCell *cell in visibleCells) {
IGListSectionController *sectionController = [self sectionControllerForCell:cell];
IGListSectionController *sectionController = [self sectionControllerForView:cell];
IGAssert(sectionController != nil, @"Section controller nil for cell %@", cell);
if (sectionController) {
[visibleSectionControllers addObject:sectionController];
Expand All @@ -410,7 +410,7 @@ - (NSArray *)visibleObjects {
NSArray<UICollectionViewCell *> *visibleCells = [self.collectionView visibleCells];
NSMutableSet *visibleObjects = [NSMutableSet new];
for (UICollectionViewCell *cell in visibleCells) {
IGListSectionController<IGListSectionType> *sectionController = [self sectionControllerForCell:cell];
IGListSectionController<IGListSectionType> *sectionController = [self sectionControllerForView:cell];
IGAssert(sectionController != nil, @"Section controller nil for cell %@", cell);
if (sectionController != nil) {
const NSInteger section = [self sectionForSectionController:sectionController];
Expand Down Expand Up @@ -626,21 +626,21 @@ - (NSIndexPath *)indexPathForSectionController:(IGListSectionController *)contro
return attributes;
}

- (void)mapCell:(UICollectionViewCell *)cell toSectionController:(IGListSectionController<IGListSectionType> *)sectionController {
- (void)mapView:(UICollectionReusableView *)view toSectionController:(IGListSectionController<IGListSectionType> *)sectionController {
IGAssertMainThread();
IGParameterAssert(cell != nil);
IGParameterAssert(view != nil);
IGParameterAssert(sectionController != nil);
[_cellSectionControllerMap setObject:sectionController forKey:cell];
[_viewSectionControllerMap setObject:sectionController forKey:view];
}

- (nullable IGListSectionController<IGListSectionType> *)sectionControllerForCell:(UICollectionViewCell *)cell {
- (nullable IGListSectionController<IGListSectionType> *)sectionControllerForView:(UICollectionReusableView *)view {
IGAssertMainThread();
return [_cellSectionControllerMap objectForKey:cell];
return [_viewSectionControllerMap objectForKey:view];
}

- (void)removeMapForCell:(UICollectionViewCell *)cell {
- (void)removeMapForView:(UICollectionReusableView *)view {
IGAssertMainThread();
[_cellSectionControllerMap removeObjectForKey:cell];
[_viewSectionControllerMap removeObjectForKey:view];
}

#pragma mark - UICollectionViewDataSource
Expand Down Expand Up @@ -669,7 +669,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
IGAssert(cell != nil, @"Returned a nil cell at indexPath <%@> from section controller: <%@>", indexPath, sectionController);

// associate the section controller with the cell so that we know which section controller is using it
[self mapCell:cell toSectionController:sectionController];
[self mapView:cell toSectionController:sectionController];

return cell;
}
Expand All @@ -679,6 +679,10 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
id <IGListSupplementaryViewSource> supplementarySource = [sectionController supplementaryViewSource];
UICollectionReusableView *view = [supplementarySource viewForSupplementaryElementOfKind:kind atIndex:indexPath.item];
IGAssert(view != nil, @"Returned a nil supplementary view at indexPath <%@> from section controller: <%@>, supplementary source: <%@>", indexPath, sectionController, supplementarySource);

// associate the section controller with the cell so that we know which section controller is using it
[self mapView:view toSectionController:sectionController];

return view;
}

Expand All @@ -703,12 +707,12 @@ - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICol
[collectionViewDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath];
}

IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForCell:cell];
IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForView:cell];
// if the section controller relationship was destroyed, reconnect it
// this happens with iOS 10 UICollectionView display range changes
if (sectionController == nil) {
sectionController = [self sectionControllerForSection:indexPath.section];
[self mapCell:cell toSectionController:sectionController];
[self mapView:cell toSectionController:sectionController];
}

id object = [self.sectionMap objectForSection:indexPath.section];
Expand All @@ -726,12 +730,42 @@ - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(
[collectionViewDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath];
}

IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForCell:cell];
IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForView:cell];
[self.displayHandler didEndDisplayingCell:cell forListAdapter:self sectionController:sectionController indexPath:indexPath];
[self.workingRangeHandler didEndDisplayingItemAtIndexPath:indexPath forListAdapter:self];

// break the association between the cell and the section controller
[self removeMapForCell:cell];
[self removeMapForView:cell];
}

- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
id<UICollectionViewDelegate> collectionViewDelegate = self.collectionViewDelegate;
if ([collectionViewDelegate respondsToSelector:@selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:)]) {
[collectionViewDelegate collectionView:collectionView willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath];
}

IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForView:view];
// if the section controller relationship was destroyed, reconnect it
// this happens with iOS 10 UICollectionView display range changes
if (sectionController == nil) {
sectionController = [self.sectionMap sectionControllerForSection:indexPath.section];
[self mapView:view toSectionController:sectionController];
}

id object = [self.sectionMap objectForSection:indexPath.section];
[self.displayHandler willDisplaySupplementaryView:view forListAdapter:self sectionController:sectionController object:object indexPath:indexPath];
}

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath {
id<UICollectionViewDelegate> collectionViewDelegate = self.collectionViewDelegate;
if ([collectionViewDelegate respondsToSelector:@selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:)]) {
[collectionViewDelegate collectionView:collectionView didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath];
}

IGListSectionController <IGListSectionType> *sectionController = [self sectionControllerForView:view];
[self.displayHandler didEndDisplayingSupplementaryView:view forListAdapter:self sectionController:sectionController indexPath:indexPath];

[self removeMapForView:view];
}

#pragma mark - UIScrollViewDelegate
Expand Down Expand Up @@ -806,7 +840,7 @@ - (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index
// only return a cell if it belongs to the section controller
// this association is created in -collectionView:cellForItemAtIndexPath:
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
if ([self sectionControllerForCell:cell] == sectionController) {
if ([self sectionControllerForView:cell] == sectionController) {
return cell;
}
}
Expand Down
48 changes: 39 additions & 9 deletions Source/Internal/IGListDisplayHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ IGLK_SUBCLASSING_RESTRICTED
@interface IGListDisplayHandler : NSObject

/**
Tells the handler that a cell will be displayed in the IGListKit infra.
Tells the handler that a cell will be displayed in the IGListAdapter.

@param cell A cell that will display.
@param listAdapter The adapter managing the infra.
@param sectionController The section controller the cell is in.
@param object The object associated with the section controller.
@param cell A cell that will be displayed.
@param listAdapter The adapter the cell will display in.
@param sectionController The section controller that manages the cell.
@param object The object that powers the section controller.
@param indexPath The index path of the cell in the UICollectionView.
*/
- (void)willDisplayCell:(UICollectionViewCell *)cell
Expand All @@ -37,18 +37,48 @@ IGLK_SUBCLASSING_RESTRICTED
indexPath:(NSIndexPath *)indexPath;

/**
Tells the handler that a cell did end display in the IGListKit infra.
Tells the handler that a cell did end display in the IGListAdapter.

@param cell A cell that did end display.
@param listAdapter The adapter managing the infra.
@param sectionController The section controller the cell is in.
@param cell A cell that will be displayed.
@param listAdapter The adapter the cell will display in.
@param sectionController The section controller that manages the cell.
@param indexPath The index path of the cell in the UICollectionView.
*/
- (void)didEndDisplayingCell:(UICollectionViewCell *)cell
forListAdapter:(IGListAdapter *)listAdapter
sectionController:(IGListSectionController<IGListSectionType> *)sectionController
indexPath:(NSIndexPath *)indexPath;


/**
Tells the handler that a supplementary view will be displayed in the IGListAdapter.

@param view A supplementary view that will be displayed.
@param listAdapter The adapter the supplementary view will display in.
@param sectionController The section controller that manages the supplementary view.
@param object The object that powers the section controller.
@param indexPath The index path of the supplementary view in the UICollectionView.
*/
- (void)willDisplaySupplementaryView:(UICollectionReusableView *)view
forListAdapter:(IGListAdapter *)listAdapter
sectionController:(IGListSectionController<IGListSectionType> *)sectionController
object:(id)object
indexPath:(NSIndexPath *)indexPath;


/**
Tells the handler that a supplementary view did end display in the IGListAdapter.

@param view A supplementary view that will be displayed.
@param listAdapter The adapter the supplementary view will display in.
@param sectionController The section controller that manages the supplementary view.
@param indexPath The index path of the supplementary view in the UICollectionView.
*/
- (void)didEndDisplayingSupplementaryView:(UICollectionReusableView *)view
forListAdapter:(IGListAdapter *)listAdapter
sectionController:(IGListSectionController<IGListSectionType> *)sectionController
indexPath:(NSIndexPath *)indexPath;

@end

NS_ASSUME_NONNULL_END
Loading