Skip to content

Commit

Permalink
IGListAdapter support for scrollViewDidEndDecelerating
Browse files Browse the repository at this point in the history
Summary:
References #842

We use -[UIScrollViewDelegate scrollViewDidEndDecelerating:] delegate callback in the app we are building to hide/show elements when motion has ended.
Closes #899

Differential Revision: D5689239

Pulled By: rnystrom

fbshipit-source-id: ea17fe94ccacc5e4f0a9e085e61f8f77cfcea3b4
  • Loading branch information
plarson authored and facebook-github-bot committed Aug 23, 2017
1 parent d936d79 commit 1717b2c
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The changelog for `IGListKit`. Also see the [releases](https://github.com/instag
- Added `-[IGListCollectionContext selectItemAtIndex:]` Select an item through IGListCollectionContext like `-[IGListCollectionContext deselectItemAtIndex:]`. [Marvin Nazari](https://github.com/MarvinNazari) (tbd)

- Added horizontal scrolling support to `IGListCollectionViewLayout`. [Peter Edmonston](https://github.com/edmonston) [(#857)](https://github.com/Instagram/IGListKit/pull/857)
- Added support for scrollViewDidEndDecelerating to `IGListAdapter`. [Phil Larson](https://github.com/plarson) [(#899)](https://github.com/Instagram/IGListKit/pull/899)

- Automatically disable `[UICollectionView isPrefetchingEnabled]` when setting a collection view on an adapter. [Ryan Nystrom](https://github.com/rnystrom) [(#889)](https://github.com/Instagram/IGListKit/pull/889)

Expand Down
14 changes: 14 additions & 0 deletions Source/IGListAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,20 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
}
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// forward this method to the delegate b/c this implementation will steal the message from the proxy
id<UIScrollViewDelegate> scrollViewDelegate = self.scrollViewDelegate;
if ([scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
[scrollViewDelegate scrollViewDidEndDecelerating:scrollView];
}
NSArray<IGListSectionController *> *visibleSectionControllers = [self visibleSectionControllers];
for (IGListSectionController *sectionController in visibleSectionControllers) {
id<IGListScrollDelegate> scrollDelegate = [sectionController scrollDelegate];
if ([scrollDelegate respondsToSelector:@selector(listAdapter:didEndDeceleratingSectionController:)]) {
[scrollDelegate listAdapter:self didEndDeceleratingSectionController:sectionController];
}
}
}

#pragma mark - IGListCollectionContext

Expand Down
11 changes: 11 additions & 0 deletions Source/IGListScrollDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ NS_SWIFT_NAME(ListScrollDelegate)
*/
- (void)listAdapter:(IGListAdapter *)listAdapter didEndDraggingSectionController:(IGListSectionController *)sectionController willDecelerate:(BOOL)decelerate;

/**
Tells the delegate that the section controller did end decelerating on screen.
@param listAdapter The list adapter whose collection view ended decelerating.
@param sectionController The visible section controller that ended decelerating.
@note This method is `@optional` until the next breaking-change release.
*/
@optional
- (void)listAdapter:(IGListAdapter *)listAdapter didEndDeceleratingSectionController:(IGListSectionController *)sectionController;

@end

NS_ASSUME_NONNULL_END
9 changes: 9 additions & 0 deletions Source/IGListStackedSectionController.m
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,15 @@ - (void)listAdapter:(IGListAdapter *)listAdapter didEndDraggingSectionController
}
}

- (void)listAdapter:(IGListAdapter *)listAdapter didEndDeceleratingSectionController:(IGListSectionController *)sectionController {
for (IGListSectionController *childSectionController in self.sectionControllers) {
id<IGListScrollDelegate> scrollDelegate = [childSectionController scrollDelegate];
if ([scrollDelegate respondsToSelector:@selector(listAdapter:didEndDeceleratingSectionController:)]) {
[scrollDelegate listAdapter:listAdapter didEndDeceleratingSectionController:childSectionController];
}
}
}

#pragma mark - IGListWorkingRangeDelegate

- (void)listAdapter:(IGListAdapter *)listAdapter sectionControllerWillEnterWorkingRange:(IGListSectionController *)sectionController {
Expand Down
3 changes: 2 additions & 1 deletion Source/Internal/IGListAdapterProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ static BOOL isInterceptedSelector(SEL sel) {
// UIScrollViewDelegate
sel == @selector(scrollViewDidScroll:) ||
sel == @selector(scrollViewWillBeginDragging:) ||
sel == @selector(scrollViewDidEndDragging:willDecelerate:)
sel == @selector(scrollViewDidEndDragging:willDecelerate:) ||
sel == @selector(scrollViewDidEndDecelerating:)
);
}

Expand Down
19 changes: 19 additions & 0 deletions Tests/IGListAdapterTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,25 @@ - (void)test_whenDidEndDragging_thatScrollViewDelegateReceivesMethod {
[mockScrollDelegate verify];
}

- (void)test_whenDidEndDecelerating_thatScrollViewDelegateReceivesMethod {
self.dataSource.objects = @[@0, @1, @2];
[self.adapter reloadDataWithCompletion:nil];

id mockCollectionDelegate = [OCMockObject mockForProtocol:@protocol(UICollectionViewDelegate)];
id mockScrollDelegate = [OCMockObject mockForProtocol:@protocol(UIScrollViewDelegate)];
self.adapter.collectionViewDelegate = mockCollectionDelegate;
self.adapter.scrollViewDelegate = mockScrollDelegate;

[[mockCollectionDelegate reject] scrollViewDidEndDecelerating:self.collectionView];
[[mockScrollDelegate expect] scrollViewDidEndDecelerating:self.collectionView];

// simulates the scrollview delegate telling the adapter that it ended decelerating
[self.adapter scrollViewDidEndDecelerating:self.collectionView];

[mockCollectionDelegate verify];
[mockScrollDelegate verify];
}

- (void)test_whenReloadingObjectsThatDontExist_thatAdapterContinues {
self.dataSource.objects = @[@0, @1, @2];
[self.adapter reloadDataWithCompletion:nil];
Expand Down
28 changes: 28 additions & 0 deletions Tests/IGListStackSectionControllerTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,34 @@ - (void)test_whenForwardingDidEndDraggingEvent_thatChildSectionControllersReceiv
[mockScrollDelegate verify];
}

- (void)test_whenForwardingDidEndDeceleratingEvent_thatChildSectionControllersReceiveEvent {
[self setupWithObjects:@[
[[IGTestObject alloc] initWithKey:@0 value:@[@1, @2, @3]],
[[IGTestObject alloc] initWithKey:@2 value:@[@1, @1]]
]];

id mockScrollDelegate = [OCMockObject mockForProtocol:@protocol(IGListScrollDelegate)];

IGListStackedSectionController *stack0 = [self.adapter sectionControllerForObject:self.dataSource.objects[0]];
IGListStackedSectionController *stack1 = [self.adapter sectionControllerForObject:self.dataSource.objects[1]];

[stack0.sectionControllers[0] setScrollDelegate:mockScrollDelegate];
[stack0.sectionControllers[1] setScrollDelegate:mockScrollDelegate];
[stack0.sectionControllers[2] setScrollDelegate:mockScrollDelegate];
[stack1.sectionControllers[0] setScrollDelegate:mockScrollDelegate];
[stack1.sectionControllers[1] setScrollDelegate:mockScrollDelegate];

[[mockScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:stack0.sectionControllers[0]];
[[mockScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:stack0.sectionControllers[1]];
[[mockScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:stack0.sectionControllers[2]];
[[mockScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:stack1.sectionControllers[0]];
[[mockScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:stack1.sectionControllers[1]];

[self.adapter scrollViewDidEndDecelerating:self.collectionView];

[mockScrollDelegate verify];
}

- (void)test_whenUsingSupplementary_withCode_thatSupplementaryViewExists {
// updater that uses reloadData so we can rebuild all views/sizes
IGListAdapter *adapter = [[IGListAdapter alloc] initWithUpdater:[IGListReloadDataUpdater new] viewController:nil];
Expand Down

0 comments on commit 1717b2c

Please sign in to comment.