mirror of
https://github.com/Instagram/IGListKit
synced 2026-05-22 08:48:21 +00:00
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:
parent
fef72e579e
commit
4eddd4a191
6 changed files with 300 additions and 63 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue