Skip to content

Commit

Permalink
Definable distance pagination for ScrollView
Browse files Browse the repository at this point in the history
  • Loading branch information
rxb committed Sep 15, 2015
1 parent 4f1ada2 commit e50059e
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Libraries/Components/ScrollView/ScrollView.js
Expand Up @@ -246,6 +246,25 @@ 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`.
*/
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)
*/
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.
Expand Down Expand Up @@ -430,6 +449,8 @@ var validAttributes = {
scrollsToTop: true,
showsHorizontalScrollIndicator: true,
showsVerticalScrollIndicator: true,
snapToInterval: true,
snapToAlignment: true,
stickyHeaderIndices: {diff: deepDiffer},
scrollEventThrottle: true,
zoomScale: true,
Expand Down
2 changes: 2 additions & 0 deletions React/Views/RCTScrollView.h
Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions React/Views/RCTScrollView.m
Expand Up @@ -620,6 +620,50 @@ - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

- (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),
Expand All @@ -631,6 +675,7 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoi
}
};
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeEnd reactTag:self.reactTag scrollView:scrollView userData:userData];

RCT_FORWARD_SCROLL_EVENT(scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset);
}

Expand Down
2 changes: 2 additions & 0 deletions React/Views/RCTScrollViewManager.m
Expand Up @@ -63,6 +63,8 @@ - (UIView *)view
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
Expand Down

0 comments on commit e50059e

Please sign in to comment.