Consider supplementary views when sending display events

Summary:
This was a little bit of an invasive change with the display handler, but I think that this is the right call. When sending display events for objects, we should account for the supplementary view as part of the section controller. This is especially useful for headers and footers.

Note that this wont effect the working range API at all.

Fixes #300
Closes https://github.com/Instagram/IGListKit/pull/470

Differential Revision: D4551338

Pulled By: rnystrom

fbshipit-source-id: dda6fbf18bcfc2c941d80ee2314a543d1ab83843
This commit is contained in:
Ryan Nystrom 2017-02-13 06:51:25 -08:00 committed by Facebook Github Bot
parent fef72e579e
commit 4eddd4a191
6 changed files with 300 additions and 63 deletions

View file

@ -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)

View file

@ -17,7 +17,7 @@
#import "IGListSectionControllerInternal.h"
@implementation IGListAdapter {
NSMapTable<UICollectionViewCell *, IGListSectionController<IGListSectionType> *> *_cellSectionControllerMap;
NSMapTable<UICollectionReusableView *, IGListSectionController<IGListSectionType> *> *_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<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];
@ -410,7 +410,7 @@
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];
@ -619,21 +619,21 @@
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
@ -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 <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;
}
@ -696,12 +700,12 @@
[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];
@ -719,12 +723,42 @@
[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
@ -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;
}
}

View file

@ -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<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

View file

@ -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<IGListSectionType> *)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<IGListSectionType> *)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<IGListSectionType> *)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<IGListSectionType> *)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<IGListSectionType> *)sectionController
object:(id)object
indexPath:(NSIndexPath *)indexPath {
IGParameterAssert(cell != nil);
IGParameterAssert(listAdapter != nil);
IGParameterAssert(object != nil);
IGParameterAssert(indexPath != nil);
id <IGListDisplayDelegate> 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<IGListSectionType> *)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 <IGListDisplayDelegate> 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

View file

@ -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<IGListSectionType> *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<IGListSectionType> *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

View file

@ -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