Definable distance pagination for ScrollView
Summary: This is an enhancement for ScrollView that adds the ability to paginate based on a width other than the width of the ScrollView itself. This is a fairly common pattern used on apps like Facebook, App Store, and Twitter to scroll through a horizontal set of cards or icons: ![img_8726 2](https://cloud.githubusercontent.com/assets/451050/8017899/39f9f47c-0bd2-11e5-9c1d-889452f20cf7.PNG) ![img_8727 2](https://cloud.githubusercontent.com/assets/451050/8017898/39f962dc-0bd2-11e5-98b4-461ac0f7f21b.PNG) ![img_8728 2](https://cloud.githubusercontent.com/assets/451050/8017900/39fd91a4-0bd2-11e5-8786-4cf0316295a0.PNG) After trying to accomplish this only with JS, it appears that attempting to take over an in-progress native scroll animation with JS is always going to result in some amount of jankiness and jumping. This pull request uses `scrollViewWillEndDragging` in RCTScrollView.m to adjust `targetContentOffset` based on two new optional props added to ScrollView. `snapToInterval` sets the multiple that the Closes https://github.com/facebook/react-native/pull/1532 Reviewed By: @svcscm, @trunkagent Differential Revision: D2443701 Pulled By: @vjeux
This commit is contained in:
parent
b9ef384f74
commit
dcf245a9a2
|
@ -246,6 +246,27 @@ var ScrollView = React.createClass({
|
|||
*/
|
||||
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number),
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
/**
|
||||
* When set, causes the scroll view to stop at multiples of the value of
|
||||
* `snapToInterval`. This can be used for paginating through children
|
||||
* that have lengths smaller than the scroll view. Used in combination
|
||||
* with `snapToAlignment`.
|
||||
* @platform ios
|
||||
*/
|
||||
snapToInterval: PropTypes.number,
|
||||
/**
|
||||
* When `snapToInterval` is set, `snapToAlignment` will define the relationship
|
||||
* of the the snapping to the scroll view.
|
||||
* - `start` (the default) will align the snap at the left (horizontal) or top (vertical)
|
||||
* - `center` will align the snap in the center
|
||||
* - `end` will align the snap at the right (horizontal) or bottom (vertical)
|
||||
* @platform ios
|
||||
*/
|
||||
snapToAlignment: PropTypes.oneOf([
|
||||
'start', // default
|
||||
'center',
|
||||
'end',
|
||||
]),
|
||||
/**
|
||||
* Experimental: When true, offscreen child views (whose `overflow` value is
|
||||
* `hidden`) are removed from their native backing superview when offscreen.
|
||||
|
@ -430,6 +451,8 @@ var validAttributes = {
|
|||
scrollsToTop: true,
|
||||
showsHorizontalScrollIndicator: true,
|
||||
showsVerticalScrollIndicator: true,
|
||||
snapToInterval: true,
|
||||
snapToAlignment: true,
|
||||
stickyHeaderIndices: {diff: deepDiffer},
|
||||
scrollEventThrottle: true,
|
||||
zoomScale: true,
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
@property (nonatomic, assign) int snapToInterval;
|
||||
@property (nonatomic, copy) NSString *snapToAlignment;
|
||||
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
||||
|
||||
@end
|
||||
|
|
|
@ -620,6 +620,48 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
|||
|
||||
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
|
||||
{
|
||||
|
||||
|
||||
// snapToInterval
|
||||
// An alternative to enablePaging which allows setting custom stopping intervals,
|
||||
// smaller than a full page size. Often seen in apps which feature horizonally
|
||||
// scrolling items. snapToInterval does not enforce scrolling one interval at a time
|
||||
// but guarantees that the scroll will stop at an interval point.
|
||||
if (self.snapToInterval) {
|
||||
CGFloat snapToIntervalF = (CGFloat)self.snapToInterval;
|
||||
|
||||
// Find which axis to snap
|
||||
BOOL isHorizontal = (scrollView.contentSize.width > self.frame.size.width);
|
||||
|
||||
// What is the current offset?
|
||||
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
|
||||
CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height;
|
||||
CGFloat alignmentOffset = 0.0f;
|
||||
if ([self.snapToAlignment isEqualToString: @"center"]) {
|
||||
alignmentOffset = (frameLength * 0.5f) + (snapToIntervalF * 0.5f);
|
||||
} else if ([self.snapToAlignment isEqualToString: @"end"]) {
|
||||
alignmentOffset = frameLength;
|
||||
}
|
||||
|
||||
// Pick snap point based on direction and proximity
|
||||
NSInteger snapIndex = floor((targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF);
|
||||
snapIndex = (translationAlongAxis < 0) ? snapIndex + 1 : snapIndex;
|
||||
CGFloat newTargetContentOffset = ( snapIndex * snapToIntervalF ) - alignmentOffset;
|
||||
|
||||
// Set new targetContentOffset
|
||||
if (isHorizontal) {
|
||||
targetContentOffset->x = newTargetContentOffset;
|
||||
} else {
|
||||
targetContentOffset->y = newTargetContentOffset;
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *userData = @{
|
||||
@"velocity": @{
|
||||
@"x": @(velocity.x),
|
||||
|
@ -631,6 +673,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
|||
}
|
||||
};
|
||||
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeEnd reactTag:self.reactTag scrollView:scrollView userData:userData];
|
||||
|
||||
RCT_FORWARD_SCROLL_EVENT(scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
|
|||
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
||||
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
|
||||
RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
|
||||
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
|
|
Loading…
Reference in New Issue