Skip to content

Commit

Permalink
Fabric: Support for <ScrollView scrollEventThrottle={...}> on iOS
Browse files Browse the repository at this point in the history
Summary: This diff adds support from `ScrollView::scrollEventThrottle` property on iOS.

Reviewed By: JoshuaGross

Differential Revision: D17000397

fbshipit-source-id: 93f23919a6a2588000c0eeca869171d1036348b6
  • Loading branch information
shergin authored and facebook-github-bot committed Aug 27, 2019
1 parent be2a252 commit 59c3d05
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@

@interface RCTScrollViewComponentView () <UIScrollViewDelegate>

@property (nonatomic, assign) CGFloat scrollEventThrottle;

@end

@implementation RCTScrollViewComponentView {
ScrollViewShadowNode::ConcreteState::Shared _state;
CGSize _contentSize;
NSTimeInterval _lastScrollEventDispatchTime;
NSTimeInterval _scrollEventThrottle;
}

+ (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view
Expand Down Expand Up @@ -59,6 +59,8 @@ - (instancetype)initWithFrame:(CGRect)frame
}];

[_scrollViewDelegateSplitter addDelegate:self];

_scrollEventThrottle = INFINITY;
}

return self;
Expand Down Expand Up @@ -107,7 +109,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
MAP_SCROLL_VIEW_PROP(scrollsToTop);
MAP_SCROLL_VIEW_PROP(showsHorizontalScrollIndicator);
MAP_SCROLL_VIEW_PROP(showsVerticalScrollIndicator);
MAP_VIEW_PROP(scrollEventThrottle);

if (oldScrollViewProps.scrollEventThrottle != newScrollViewProps.scrollEventThrottle) {
// Zero means "send value only once per significant logical event".
// Prop value is in milliseconds.
// iOS implementation uses `NSTimeInterval` (in seconds).
// 16 ms is the minimum allowed value.
_scrollEventThrottle = newScrollViewProps.scrollEventThrottle <= 0
? INFINITY
: std::max(newScrollViewProps.scrollEventThrottle / 1000.0, 1.0 / 60.0);
}

MAP_SCROLL_VIEW_PROP(zoomScale);

if (oldScrollViewProps.contentInset != newScrollViewProps.contentInset) {
Expand Down Expand Up @@ -184,87 +196,108 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScroll([self _scrollViewMetrics]);
NSTimeInterval now = CACurrentMediaTime();
if ((_lastScrollEventDispatchTime == 0) || (now - _lastScrollEventDispatchTime > _scrollEventThrottle)) {
_lastScrollEventDispatchTime = now;
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onScroll([self _scrollViewMetrics]);
}
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScroll([self _scrollViewMetrics]);
[self scrollViewDidScroll:scrollView];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScrollBeginDrag([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onScrollBeginDrag([self _scrollViewMetrics]);
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScrollEndDrag([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onScrollEndDrag([self _scrollViewMetrics]);
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)
->onMomentumScrollBegin([self _scrollViewMetrics]);
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onMomentumScrollEnd([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onMomentumScrollEnd([self _scrollViewMetrics]);
[self _updateStateWithContentOffset];
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onMomentumScrollEnd([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onMomentumScrollEnd([self _scrollViewMetrics]);
[self _updateStateWithContentOffset];
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScrollBeginDrag([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onScrollBeginDrag([self _scrollViewMetrics]);
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale
{
[self _forceDispatchNextScrollEvent];

if (!_eventEmitter) {
return;
}

std::static_pointer_cast<const ScrollViewEventEmitter>(_eventEmitter)->onScrollEndDrag([self _scrollViewMetrics]);
std::static_pointer_cast<ScrollViewEventEmitter const>(_eventEmitter)->onScrollEndDrag([self _scrollViewMetrics]);
[self _updateStateWithContentOffset];
}

#pragma mark - UIScrollViewDelegate

- (void)_forceDispatchNextScrollEvent
{
_lastScrollEventDispatchTime = 0;
}

@end

@implementation RCTScrollViewComponentView (ScrollableProtocol)
Expand All @@ -276,11 +309,13 @@ - (CGSize)contentSize

- (void)scrollToOffset:(CGPoint)offset
{
[self _forceDispatchNextScrollEvent];
[self scrollToOffset:offset animated:YES];
}

- (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated
{
[self _forceDispatchNextScrollEvent];
[self.scrollView setContentOffset:offset animated:animated];
}

Expand Down
2 changes: 1 addition & 1 deletion ReactCommon/fabric/components/scrollview/ScrollViewProps.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ScrollViewProps final : public ViewProps {
const bool scrollsToTop{true};
const bool showsHorizontalScrollIndicator{true};
const bool showsVerticalScrollIndicator{true};
const Float scrollEventThrottle{};
const int scrollEventThrottle{};
const Float zoomScale{1.0};
const EdgeInsets contentInset{};
const EdgeInsets scrollIndicatorInsets{};
Expand Down

0 comments on commit 59c3d05

Please sign in to comment.