Improve feel of scroll snapping behavior on iOS

Reviewed By: sahrens, shergin

Differential Revision: D4768384

fbshipit-source-id: 6319d058292be6ba38c059d8d1a1bf70bac603e2
This commit is contained in:
Ben Alpert 2017-03-29 06:59:45 -07:00 committed by Facebook Github Bot
parent c233191485
commit 1d367004e9

View File

@ -326,10 +326,6 @@ static inline BOOL isRectInvalid(CGRect rect) {
uint16_t _coalescingKey; uint16_t _coalescingKey;
NSString *_lastEmittedEventName; NSString *_lastEmittedEventName;
NSHashTable *_scrollListeners; NSHashTable *_scrollListeners;
// The last non-zero value of translationAlongAxis from scrollViewWillEndDragging.
// Tells if user was scrolling forward or backward and is used to determine a correct
// snap index when the user stops scrolling with a tap on the scroll view.
CGFloat _lastNonZeroTranslationAlongAxis;
} }
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
@ -667,32 +663,27 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
BOOL isHorizontal = [self isHorizontal:scrollView]; BOOL isHorizontal = [self isHorizontal:scrollView];
// What is the current offset? // What is the current offset?
CGFloat velocityAlongAxis = isHorizontal ? velocity.x : velocity.y;
CGFloat targetContentOffsetAlongAxis = isHorizontal ? targetContentOffset->x : targetContentOffset->y; CGFloat targetContentOffsetAlongAxis = isHorizontal ? targetContentOffset->x : targetContentOffset->y;
// Which direction is the scroll travelling?
CGPoint translation = [scrollView.panGestureRecognizer translationInView:scrollView];
CGFloat translationAlongAxis = isHorizontal ? translation.x : translation.y;
// Offset based on desired alignment // Offset based on desired alignment
CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height; CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height;
CGFloat alignmentOffset = 0.0f; CGFloat alignmentOffset = 0.0f;
if ([self.snapToAlignment isEqualToString: @"center"]) { if ([self.snapToAlignment isEqualToString: @"center"]) {
alignmentOffset = (frameLength * 0.5f) + (snapToIntervalF * 0.5f); alignmentOffset = (frameLength * 0.5f) + (snapToIntervalF * 0.5f);
} else if ([self.snapToAlignment isEqualToString: @"end"]) { } else if ([self.snapToAlignment isEqualToString: @"end"]) {
alignmentOffset = frameLength; alignmentOffset = frameLength;
} }
// Pick snap point based on direction and proximity // Pick snap point based on direction and proximity
NSInteger snapIndex = floor((targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF); CGFloat fractionalIndex = (targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF;
BOOL isScrollingForward = translationAlongAxis < 0; NSInteger snapIndex =
BOOL wasScrollingForward = translationAlongAxis == 0 && _lastNonZeroTranslationAlongAxis < 0; velocityAlongAxis > 0.0 ?
if (isScrollingForward || wasScrollingForward) { ceil(fractionalIndex) :
snapIndex = snapIndex + 1; velocityAlongAxis < 0.0 ?
} floor(fractionalIndex) :
if (translationAlongAxis != 0) { round(fractionalIndex);
_lastNonZeroTranslationAlongAxis = translationAlongAxis; CGFloat newTargetContentOffset = (snapIndex * snapToIntervalF) - alignmentOffset;
}
CGFloat newTargetContentOffset = ( snapIndex * snapToIntervalF ) - alignmentOffset;
// Set new targetContentOffset // Set new targetContentOffset
if (isHorizontal) { if (isHorizontal) {