Working ranges in stacked section controllers

Summary:
Closes #354

- [x] All tests pass. Demo project builds and runs.
- [x] I added tests, an experiment, or detailed why my change isn't tested.
- [x] I have reviewed the [contributing guide](https://github.com/Instagram/IGListKit/blob/master/.github/CONTRIBUTING.md)
Closes https://github.com/Instagram/IGListKit/pull/356

Differential Revision: D4365718

Pulled By: jessesquires

fbshipit-source-id: 41d7bcd823415e5795a069a4ef335e60e7da806a
This commit is contained in:
Ryan Nystrom 2016-12-23 08:18:02 -08:00 committed by Facebook Github Bot
parent c3c0e36144
commit e9ad6b199e
4 changed files with 71 additions and 2 deletions

View file

@ -15,6 +15,8 @@ This release closes the [2.1.0 milestone](https://github.com/Instagram/IGListKit
- Disables `prefetchEnabled` by default on `IGListCollectionView`. [Sven Bacia](https://github.com/svenbacia) [(#323)](https://github.com/Instagram/IGListKit/pull/323)
- Working ranges now work with `IGListStackedSectionController`. [Ryan Nystrom](https://github.com/rnystrom) [(#356)](https://github.com/Instagram/IGListKit/pull/356)
### Fixes
- Avoid `UICollectionView` crashes when queueing a reload and insert/delete on the same item as well as reloading an item in a section that is animating. [Ryan Nystrom](https://github.com/rnystrom) [(#325)](https://github.com/Instagram/IGListKit/pull/325)

View file

@ -56,6 +56,7 @@ static void * kStackedSectionControllerIndexKey = &kStackedSectionControllerInde
self.displayDelegate = self;
self.scrollDelegate = self;
self.workingRangeDelegate = self;
[self reloadData];
}
@ -363,4 +364,18 @@ static void * kStackedSectionControllerIndexKey = &kStackedSectionControllerInde
}
}
#pragma mark - IGListWorkingRangeDelegate
- (void)listAdapter:(IGListAdapter *)listAdapter sectionControllerWillEnterWorkingRange:(IGListSectionController<IGListSectionType> *)sectionController {
for (IGListSectionController<IGListSectionType> *childSectionController in self.sectionControllers) {
[[childSectionController workingRangeDelegate] listAdapter:listAdapter sectionControllerWillEnterWorkingRange:childSectionController];
}
}
- (void)listAdapter:(IGListAdapter *)listAdapter sectionControllerDidExitWorkingRange:(IGListSectionController<IGListSectionType> *)sectionController {
for (IGListSectionController<IGListSectionType> *childSectionController in self.sectionControllers) {
[[childSectionController workingRangeDelegate] listAdapter:listAdapter sectionControllerDidExitWorkingRange:childSectionController];
}
}
@end

View file

@ -15,7 +15,8 @@
<
IGListCollectionContext,
IGListDisplayDelegate,
IGListScrollDelegate
IGListScrollDelegate,
IGListWorkingRangeDelegate
>
@property (nonatomic, strong, readonly) NSOrderedSet<__kindof IGListSectionController<IGListSectionType> *> *sectionControllers;

View file

@ -54,7 +54,7 @@ static const CGRect kStackTestFrame = (CGRect){{0.0, 0.0}, {100.0, 100.0}};
self.collectionView.frame = kStackTestFrame;
self.dataSource = [[IGTestStackedDataSource alloc] init];
self.adapter = [[IGListAdapter alloc] initWithUpdater:[IGListAdapterUpdater new] viewController:nil workingRangeSize:0];
self.adapter = [[IGListAdapter alloc] initWithUpdater:[IGListAdapterUpdater new] viewController:nil workingRangeSize:1];
}
- (void)tearDown {
@ -702,6 +702,57 @@ static const CGRect kStackTestFrame = (CGRect){{0.0, 0.0}, {100.0, 100.0}};
XCTAssertFalse([[self.collectionView cellForItemAtIndexPath:path] isSelected]);
}
- (void)test_whenRemovingSection_withWorkingRange_thatChildSectionControllersReceiveEvents {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@1 value:@[@1, @1]]
]];
IGListStackedSectionController *stack = [self.adapter sectionControllerForObject:self.dataSource.objects.firstObject];
IGListTestSection *section = stack.sectionControllers.firstObject;
id mockDelegate = [OCMockObject mockForProtocol:@protocol(IGListWorkingRangeDelegate)];
[[mockDelegate expect] listAdapter:self.adapter sectionControllerDidExitWorkingRange:section];
section.workingRangeDelegate = mockDelegate;
self.dataSource.objects = @[
[[IGTestObject alloc] initWithKey:@1 value:@[@1, @1]],
];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.adapter performUpdatesAnimated:YES completion:^(BOOL finished) {
[mockDelegate verify];
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:15 handler:nil];
}
- (void)test_whenScrolling_withWorkingRange_thatChildSectionControllersReceiveEvents {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@1 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@3 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@4 value:@[@1, @1]]
]];
IGListStackedSectionController *stack = [self.adapter sectionControllerForObject:self.dataSource.objects.lastObject];
IGListTestSection *section = stack.sectionControllers.firstObject;
id mockDelegate = [OCMockObject mockForProtocol:@protocol(IGListWorkingRangeDelegate)];
[[mockDelegate expect] listAdapter:self.adapter sectionControllerWillEnterWorkingRange:section];
section.workingRangeDelegate = mockDelegate;
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:4] atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
[self.collectionView layoutIfNeeded];
[mockDelegate verify];
}
- (void)test_whenRemovingCellsFromChild_thatStackSendsDisplayEventsCorrectly {
IGTestObject *object = [[IGTestObject alloc] initWithKey:@0 value:@[@1, @2]];
[self setupWithObjects:@[object]];