diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a54297e..f6f02054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,6 +122,10 @@ You can find a [migration guide here](https://instagram.github.io/IGListKit/migr animated:(BOOL)animated; ``` +### Fixes + +- Consider supplementary views with display and end-display events. [Ryan Nystrom](https://github.com/rnystrom) [(#470)](https://github.com/Instagram/IGListKit/pull/470) + - Changed `NSUInteger` to `NSInteger` in all public APIs. [Suraya Shivji](https://github.com/surayashivji) [(#200)](https://github.com/Instagram/IGListKit/issues/200) diff --git a/Source/IGListAdapter.m b/Source/IGListAdapter.m index bee238a6..989ffae2 100644 --- a/Source/IGListAdapter.m +++ b/Source/IGListAdapter.m @@ -17,7 +17,7 @@ #import "IGListSectionControllerInternal.h" @implementation IGListAdapter { - NSMapTable *> *_cellSectionControllerMap; + NSMapTable *> *_viewSectionControllerMap; BOOL _isDequeuingCell; BOOL _isSendingWorkingRangeDisplayUpdates; } @@ -49,7 +49,7 @@ _displayHandler = [[IGListDisplayHandler alloc] init]; _workingRangeHandler = [[IGListWorkingRangeHandler alloc] initWithWorkingRangeSize:workingRangeSize]; - _cellSectionControllerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality | NSMapTableStrongMemory + _viewSectionControllerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableObjectPointerPersonality | NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory]; _updater = updater; @@ -396,7 +396,7 @@ NSArray *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]; @@ -410,7 +410,7 @@ NSArray *visibleCells = [self.collectionView visibleCells]; NSMutableSet *visibleObjects = [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 != nil) { const NSInteger section = [self sectionForSectionController:sectionController]; @@ -619,21 +619,21 @@ return attributes; } -- (void)mapCell:(UICollectionViewCell *)cell toSectionController:(IGListSectionController *)sectionController { +- (void)mapView:(UICollectionReusableView *)view toSectionController:(IGListSectionController *)sectionController { IGAssertMainThread(); - IGParameterAssert(cell != nil); + IGParameterAssert(view != nil); IGParameterAssert(sectionController != nil); - [_cellSectionControllerMap setObject:sectionController forKey:cell]; + [_viewSectionControllerMap setObject:sectionController forKey:view]; } -- (nullable IGListSectionController *)sectionControllerForCell:(UICollectionViewCell *)cell { +- (nullable IGListSectionController *)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 @@ -662,7 +662,7 @@ 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; } @@ -672,6 +672,10 @@ id 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; } @@ -696,12 +700,12 @@ [collectionViewDelegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath]; } - IGListSectionController *sectionController = [self sectionControllerForCell:cell]; + IGListSectionController *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]; @@ -719,12 +723,42 @@ [collectionViewDelegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; } - IGListSectionController *sectionController = [self sectionControllerForCell:cell]; + IGListSectionController *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 collectionViewDelegate = self.collectionViewDelegate; + if ([collectionViewDelegate respondsToSelector:@selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:)]) { + [collectionViewDelegate collectionView:collectionView willDisplaySupplementaryView:view forElementKind:elementKind atIndexPath:indexPath]; + } + + IGListSectionController *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 collectionViewDelegate = self.collectionViewDelegate; + if ([collectionViewDelegate respondsToSelector:@selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:)]) { + [collectionViewDelegate collectionView:collectionView didEndDisplayingSupplementaryView:view forElementOfKind:elementKind atIndexPath:indexPath]; + } + + IGListSectionController *sectionController = [self sectionControllerForView:view]; + [self.displayHandler didEndDisplayingSupplementaryView:view forListAdapter:self sectionController:sectionController indexPath:indexPath]; + + [self removeMapForView:view]; } #pragma mark - UIScrollViewDelegate @@ -799,7 +833,7 @@ // 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; } } diff --git a/Source/Internal/IGListDisplayHandler.h b/Source/Internal/IGListDisplayHandler.h index c27644c9..3f43d574 100644 --- a/Source/Internal/IGListDisplayHandler.h +++ b/Source/Internal/IGListDisplayHandler.h @@ -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 @@ -37,11 +37,11 @@ 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 @@ -49,6 +49,36 @@ IGLK_SUBCLASSING_RESTRICTED sectionController:(IGListSectionController *)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 *)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 *)sectionController + indexPath:(NSIndexPath *)indexPath; + @end NS_ASSUME_NONNULL_END diff --git a/Source/Internal/IGListDisplayHandler.m b/Source/Internal/IGListDisplayHandler.m index 20cdec82..5dd7564a 100644 --- a/Source/Internal/IGListDisplayHandler.m +++ b/Source/Internal/IGListDisplayHandler.m @@ -17,7 +17,7 @@ @interface IGListDisplayHandler () @property (nonatomic, strong) NSCountedSet *visibleListSections; -@property (nonatomic, strong) NSMapTable *visibleCellObjectMap; +@property (nonatomic, strong) NSMapTable *visibleViewObjectMap; @end @@ -26,61 +26,100 @@ - (instancetype)init { if (self = [super init]) { _visibleListSections = [[NSCountedSet alloc] init]; - _visibleCellObjectMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:0]; + _visibleViewObjectMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:0]; } return self; } +- (id)pluckObjectForView:(UICollectionReusableView *)view { + NSMapTable *viewObjectMap = self.visibleViewObjectMap; + id object = [viewObjectMap objectForKey:view]; + [viewObjectMap removeObjectForKey:view]; + return object; +} + +- (void)willDisplayReusableView:(UICollectionReusableView *)view + forListAdapter:(IGListAdapter *)listAdapter + sectionController:(IGListSectionController *)sectionController + object:(id)object + indexPath:(NSIndexPath *)indexPath { + IGParameterAssert(view != nil); + IGParameterAssert(listAdapter != nil); + IGParameterAssert(object != nil); + IGParameterAssert(indexPath != nil); + + [self.visibleViewObjectMap setObject:object forKey:view]; + NSCountedSet *visibleListSections = self.visibleListSections; + if ([visibleListSections countForObject:sectionController] == 0) { + [sectionController.displayDelegate listAdapter:listAdapter willDisplaySectionController:sectionController]; + [listAdapter.delegate listAdapter:listAdapter willDisplayObject:object atIndex:indexPath.section]; + } + [visibleListSections addObject:sectionController]; +} + +- (void)didEndDisplayingReusableView:(UICollectionReusableView *)view + forListAdapter:(IGListAdapter *)listAdapter + sectionController:(IGListSectionController *)sectionController + object:(id)object + indexPath:(NSIndexPath *)indexPath { + IGParameterAssert(view != nil); + IGParameterAssert(listAdapter != nil); + IGParameterAssert(indexPath != nil); + + if (object == nil || sectionController == nil) { + return; + } + + const NSInteger section = indexPath.section; + + NSCountedSet *visibleSections = self.visibleListSections; + [visibleSections removeObject:sectionController]; + + if ([visibleSections countForObject:sectionController] == 0) { + [sectionController.displayDelegate listAdapter:listAdapter didEndDisplayingSectionController:sectionController]; + [listAdapter.delegate listAdapter:listAdapter didEndDisplayingObject:object atIndex:section]; + } +} + +- (void)willDisplaySupplementaryView:(UICollectionReusableView *)view + forListAdapter:(IGListAdapter *)listAdapter + sectionController:(IGListSectionController *)sectionController + object:(id)object + indexPath:(NSIndexPath *)indexPath { + [self willDisplayReusableView:view forListAdapter:listAdapter sectionController:sectionController object:object indexPath:indexPath]; +} + +- (void)didEndDisplayingSupplementaryView:(UICollectionReusableView *)view + forListAdapter:(IGListAdapter *)listAdapter + sectionController:(IGListSectionController *)sectionController + indexPath:(NSIndexPath *)indexPath { + // if cell display events break, don't send display events when the object has disappeared + id object = [self pluckObjectForView:view]; + [self didEndDisplayingReusableView:view forListAdapter:listAdapter sectionController:sectionController object:object indexPath:indexPath]; +} + - (void)willDisplayCell:(UICollectionViewCell *)cell forListAdapter:(IGListAdapter *)listAdapter sectionController:(IGListSectionController *)sectionController object:(id)object indexPath:(NSIndexPath *)indexPath { - IGParameterAssert(cell != nil); - IGParameterAssert(listAdapter != nil); - IGParameterAssert(object != nil); - IGParameterAssert(indexPath != nil); - id displayDelegate = [sectionController displayDelegate]; - [displayDelegate listAdapter:listAdapter willDisplaySectionController:sectionController cell:cell atIndex:indexPath.item]; - - [self.visibleCellObjectMap setObject:object forKey:cell]; - - if ([self.visibleListSections countForObject:sectionController] == 0) { - [displayDelegate listAdapter:listAdapter willDisplaySectionController:sectionController]; - [listAdapter.delegate listAdapter:listAdapter willDisplayObject:object atIndex:indexPath.section]; - } - [self.visibleListSections addObject:sectionController]; + [self willDisplayReusableView:cell forListAdapter:listAdapter sectionController:sectionController object:object indexPath:indexPath]; } - (void)didEndDisplayingCell:(UICollectionViewCell *)cell forListAdapter:(IGListAdapter *)listAdapter sectionController:(IGListSectionController *)sectionController indexPath:(NSIndexPath *)indexPath { - IGParameterAssert(cell != nil); - IGParameterAssert(listAdapter != nil); - IGParameterAssert(indexPath != nil); - - const NSInteger section = indexPath.section; - - NSMapTable *cellObjectMap = self.visibleCellObjectMap; - id object = [cellObjectMap objectForKey:cell]; - [cellObjectMap removeObjectForKey:cell]; - - if (object == nil || sectionController == nil) { + // if cell display events break, don't send cell events to the displayDelegate when the object has disappeared + id object = [self pluckObjectForView:cell]; + if (object == nil) { return; } - id displayDelegate = [sectionController displayDelegate]; - [displayDelegate listAdapter:listAdapter didEndDisplayingSectionController:sectionController cell:cell atIndex:indexPath.item]; - - NSCountedSet *visibleSections = self.visibleListSections; - [visibleSections removeObject:sectionController]; - if ([visibleSections countForObject:sectionController] == 0) { - [displayDelegate listAdapter:listAdapter didEndDisplayingSectionController:sectionController]; - [listAdapter.delegate listAdapter:listAdapter didEndDisplayingObject:object atIndex:section]; - } + [sectionController.displayDelegate listAdapter:listAdapter didEndDisplayingSectionController:sectionController cell:cell atIndex:indexPath.item]; + [self didEndDisplayingReusableView:cell forListAdapter:listAdapter sectionController:sectionController object:object indexPath:indexPath]; } @end diff --git a/Tests/IGListAdapterTests.m b/Tests/IGListAdapterTests.m index acc5d28b..fb37aa6a 100644 --- a/Tests/IGListAdapterTests.m +++ b/Tests/IGListAdapterTests.m @@ -976,4 +976,88 @@ XCTAssertEqual(CGPointEqualToPoint(point, p), YES); \ XCTAssertEqual(self.collectionView.contentOffset.y, 280); } +- (void)test_whenDisplayingSectionController_withOnlySupplementaryView_thatDisplayEventStillSent { + self.dataSource.objects = @[@0]; + [self.adapter reloadDataWithCompletion:nil]; + XCTAssertNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]); + + IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new]; + supplementarySource.collectionContext = self.adapter; + supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionHeader]; + + IGListSectionController *controller = [self.adapter sectionControllerForObject:@0]; + controller.supplementaryViewSource = supplementarySource; + supplementarySource.sectionController = controller; + + id mockDisplayDelegate = [OCMockObject mockForProtocol:@protocol(IGListDisplayDelegate)]; + [[mockDisplayDelegate expect] listAdapter:self.adapter willDisplaySectionController:controller]; + [[mockDisplayDelegate reject] listAdapter:self.adapter willDisplaySectionController:controller cell:[OCMArg any] atIndex:0]; + + controller.displayDelegate = mockDisplayDelegate; + + [self.adapter performUpdatesAnimated:NO completion:nil]; + XCTAssertNotNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]); + + [mockDisplayDelegate verify]; +} + +- (void)test_whenEndingDisplayOfSectionController_withOnlySupplementaryView_thatDisplayEventStillSent { + self.dataSource.objects = @[@0]; + [self.adapter reloadDataWithCompletion:nil]; + XCTAssertNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]); + + IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new]; + supplementarySource.collectionContext = self.adapter; + supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionHeader]; + + IGListSectionController *controller = [self.adapter sectionControllerForObject:@0]; + controller.supplementaryViewSource = supplementarySource; + supplementarySource.sectionController = controller; + + [self.adapter performUpdatesAnimated:NO completion:nil]; + XCTAssertNotNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]); + + id mockDisplayDelegate = [OCMockObject mockForProtocol:@protocol(IGListDisplayDelegate)]; + [[mockDisplayDelegate expect] listAdapter:self.adapter didEndDisplayingSectionController:controller]; + [[mockDisplayDelegate reject] listAdapter:self.adapter didEndDisplayingSectionController:controller cell:[OCMArg any] atIndex:0]; + + controller.displayDelegate = mockDisplayDelegate; + + controller.supplementaryViewSource = nil; + [self.adapter performUpdatesAnimated:NO completion:nil]; + XCTAssertNil([self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]); + + [mockDisplayDelegate verify]; +} + +- (void)test_whenWillDisplaySupplementaryView_thatCollectionViewDelegateReceivesEvents { + // silence display handler asserts + self.dataSource.objects = @[@1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + id mockDelegate = [OCMockObject mockForProtocol:@protocol(UICollectionViewDelegate)]; + self.adapter.collectionViewDelegate = mockDelegate; + UICollectionReusableView *view = [UICollectionReusableView new]; + NSString *kind = @"kind"; + NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; + [[mockDelegate expect] collectionView:self.collectionView willDisplaySupplementaryView:view forElementKind:kind atIndexPath:path]; + [self.adapter collectionView:self.collectionView willDisplaySupplementaryView:view forElementKind:kind atIndexPath:path]; + [mockDelegate verify]; +} + +- (void)test_whenEndDisplayingSupplementaryView_thatCollectionViewDelegateReceivesEvents { + // silence display handler asserts + self.dataSource.objects = @[@1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + id mockDelegate = [OCMockObject mockForProtocol:@protocol(UICollectionViewDelegate)]; + self.adapter.collectionViewDelegate = mockDelegate; + UICollectionReusableView *view = [UICollectionReusableView new]; + NSString *kind = @"kind"; + NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; + [[mockDelegate expect] collectionView:self.collectionView didEndDisplayingSupplementaryView:view forElementOfKind:kind atIndexPath:path]; + [self.adapter collectionView:self.collectionView didEndDisplayingSupplementaryView:view forElementOfKind:kind atIndexPath:path]; + [mockDelegate verify]; +} + @end diff --git a/Tests/IGListDisplayHandlerTests.m b/Tests/IGListDisplayHandlerTests.m index 23165252..2e640654 100644 --- a/Tests/IGListDisplayHandlerTests.m +++ b/Tests/IGListDisplayHandlerTests.m @@ -176,7 +176,6 @@ [self.mockAdapterDelegate verify]; } - - (void)test_whenCellInserted_withDisplayedCellExistingAtPath_thatDisplayHandlerReceivesCorrectParams { // simulate first cell appearing in the collection view NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; @@ -199,4 +198,51 @@ [self.mockDisplayDelegate verify]; } +- (void)test_whenWillDisplaySupplementaryView_withCellDisplayedAfter_thatDisplayHandlerReceivesOneEvent { + NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; + UICollectionReusableView *view = [UICollectionReusableView new]; + UICollectionViewCell *cell = [UICollectionViewCell new]; + + self.list.displayDelegate = self.mockDisplayDelegate; + self.adapter.delegate = self.mockAdapterDelegate; + + [[self.mockDisplayDelegate expect] listAdapter:self.adapter willDisplaySectionController:self.list]; + [[self.mockAdapterDelegate expect] listAdapter:self.adapter willDisplayObject:self.object atIndex:path.section]; + + [self.displayHandler willDisplaySupplementaryView:view forListAdapter:self.adapter sectionController:self.list object:self.object indexPath:path]; + + [self.mockDisplayDelegate verify]; + [self.mockAdapterDelegate verify]; + + [[self.mockDisplayDelegate expect] listAdapter:self.adapter willDisplaySectionController:self.list cell:cell atIndex:path.item]; + [[self.mockAdapterDelegate reject] listAdapter:self.adapter willDisplayObject:self.list atIndex:path.item]; + [[self.mockDisplayDelegate reject] listAdapter:self.adapter willDisplaySectionController:self.list]; + + [self.displayHandler willDisplayCell:cell forListAdapter:self.adapter sectionController:self.list object:self.object indexPath:path]; +} + +- (void)test_whenEndDisplayingSupplementaryView_withEndDisplayingTwice_thatDisplayHandlerReceivesOneEvent { + NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; + UICollectionReusableView *view = [UICollectionReusableView new]; + + [self.displayHandler willDisplaySupplementaryView:view forListAdapter:self.adapter sectionController:self.list object:self.object indexPath:path]; + + [[self.mockDisplayDelegate expect] listAdapter:self.adapter didEndDisplayingSectionController:self.list]; + [[self.mockAdapterDelegate expect] listAdapter:self.adapter didEndDisplayingObject:self.object atIndex:path.section]; + + [[self.mockDisplayDelegate reject] listAdapter:self.adapter didEndDisplayingSectionController:self.list]; + [[self.mockDisplayDelegate reject] listAdapter:self.adapter didEndDisplayingSectionController:self.list cell:[OCMArg any] atIndex:path.item]; + [[self.mockAdapterDelegate reject] listAdapter:self.adapter didEndDisplayingObject:self.object atIndex:path.section]; + + self.list.displayDelegate = self.mockDisplayDelegate; + self.adapter.delegate = self.mockAdapterDelegate; + //first call + [self.displayHandler didEndDisplayingSupplementaryView:view forListAdapter:self.adapter sectionController:self.list indexPath:path]; + //second call + [self.displayHandler didEndDisplayingSupplementaryView:view forListAdapter:self.adapter sectionController:self.list indexPath:path]; + + [self.mockDisplayDelegate verify]; + [self.mockAdapterDelegate verify]; +} + @end