rename and extend new maintain visible content position feature

Summary:
Builds off of cae7179c94

- Make the prop a dictionary for more configuration options
- Rename `maintainPositionAtOrBeyondIndex` -> `maintainVisibleContentPosition` + `minIndexForVisible`
- Add autoscroll threshold feature

Given the async native of RN JS and background layout, there is no way to trigger the scrollTo from JS without risking a delay, so we add the feature in native code.

== Test Plan ==
ScrollViewExample:
https://youtu.be/pmY8pxC9PRs

Reviewed By: shergin

Differential Revision: D6729160

fbshipit-source-id: 70f9bae460ce84567857a4f696da78ce9b3b834c
This commit is contained in:
Spencer Ahrens
2018-01-18 13:51:43 -08:00
committed by Facebook Github Bot
parent 7e7d00aebe
commit 65184ec6b0
5 changed files with 56 additions and 22 deletions

View File

@@ -911,16 +911,16 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
}
}
// maintainPositionAtOrBeyondIndex is used to allow seamless loading of content from both ends of
// maintainVisibleContentPosition is used to allow seamless loading of content from both ends of
// the scrollview without the visible content jumping in position.
- (void)setMaintainPositionAtOrBeyondIndex:(NSNumber *)maintainPositionAtOrBeyondIndex
- (void)setMaintainVisibleContentPosition:(NSDictionary *)maintainVisibleContentPosition
{
if (maintainPositionAtOrBeyondIndex != nil) {
if (maintainVisibleContentPosition != nil && _maintainVisibleContentPosition == nil) {
[_eventDispatcher.bridge.uiManager.observerCoordinator addObserver:self];
} else {
} else if (maintainVisibleContentPosition == nil && _maintainVisibleContentPosition != nil) {
[_eventDispatcher.bridge.uiManager.observerCoordinator removeObserver:self];
}
_maintainPositionAtOrBeyondIndex = maintainPositionAtOrBeyondIndex;
_maintainVisibleContentPosition = maintainVisibleContentPosition;
}
#pragma mark - RCTUIManagerObserver
@@ -930,7 +930,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
RCTAssertUIManagerQueue();
[manager prependUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
BOOL horz = [self isHorizontal:self->_scrollView];
NSUInteger minIdx = [self->_maintainPositionAtOrBeyondIndex integerValue];
NSUInteger minIdx = [self->_maintainVisibleContentPosition[@"minIndexForVisible"] integerValue];
for (NSUInteger ii = minIdx; ii < self->_contentView.subviews.count; ++ii) {
// Find the first entirely visible view. This must be done after we update the content offset
// or it will tend to grab rows that were made visible by the shift in position
@@ -946,9 +946,10 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
}
}];
[manager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
if (self->_maintainPositionAtOrBeyondIndex == nil) {
if (self->_maintainVisibleContentPosition == nil) {
return; // The prop might have changed in the previous UIBlocks, so need to abort here.
}
NSNumber *autoscrollThreshold = self->_maintainVisibleContentPosition[@"autoscrollToTopThreshold"];
// TODO: detect and handle/ignore re-ordering
if ([self isHorizontal:self->_scrollView]) {
CGFloat deltaX = self->_firstVisibleView.frame.origin.x - self->_prevFirstVisibleFrame.origin.x;
@@ -957,15 +958,27 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
self->_scrollView.contentOffset.x + deltaX,
self->_scrollView.contentOffset.y
);
if (autoscrollThreshold != nil) {
// If the offset WAS within the threshold of the start, animate to the start.
if (self->_scrollView.contentOffset.x - deltaX <= [autoscrollThreshold integerValue]) {
[self scrollToOffset:CGPointMake(0, self->_scrollView.contentOffset.y) animated:YES];
}
}
}
} else {
CGRect newFrame = self->_firstVisibleView.frame;
CGFloat deltaY = newFrame.origin.y - self->_prevFirstVisibleFrame.origin.y;
if (ABS(deltaY) > 0.1 || deltaY != 0.0) {
if (ABS(deltaY) > 0.1) {
self->_scrollView.contentOffset = CGPointMake(
self->_scrollView.contentOffset.x,
self->_scrollView.contentOffset.y + deltaY
);
if (autoscrollThreshold != nil) {
// If the offset WAS within the threshold of the start, animate to the start.
if (self->_scrollView.contentOffset.y - deltaY <= [autoscrollThreshold integerValue]) {
[self scrollToOffset:CGPointMake(self->_scrollView.contentOffset.x, 0) animated:YES];
}
}
}
}
}];