diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index 85e8ede5c..33db538ab 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -380,6 +380,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) CGRect _lastClippedToRect; uint16_t _coalescingKey; NSString *_lastEmittedEventName; + NSHashTable *_scrollListeners; } @synthesize nativeScrollDelegate = _nativeScrollDelegate; @@ -402,6 +403,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) _lastScrollDispatchTime = 0; _cachedChildFrames = [NSMutableArray new]; + _scrollListeners = [NSHashTable weakObjectsHashTable]; + [self addSubview:_scrollView]; } return self; @@ -570,8 +573,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) } #define RCT_FORWARD_SCROLL_EVENT(call) \ -if ([_nativeScrollDelegate respondsToSelector:_cmd]) { \ - [_nativeScrollDelegate call]; \ +for (NSObject *scrollViewListener in _scrollListeners) { \ + if ([scrollViewListener respondsToSelector:_cmd]) { \ + [scrollViewListener call]; \ + } \ } #define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \ @@ -584,6 +589,33 @@ if ([_nativeScrollDelegate respondsToSelector:_cmd]) { \ RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin) RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) +- (void)setNativeScrollDelegate:(NSObject *)nativeScrollDelegate +{ + if (nativeScrollDelegate != _nativeScrollDelegate) { + if (_nativeScrollDelegate) { + [_scrollListeners removeObject:_nativeScrollDelegate]; + } + if (nativeScrollDelegate) { + [_scrollListeners addObject:nativeScrollDelegate]; + } + _nativeScrollDelegate = nativeScrollDelegate; + } +} + +- (void)addScrollListener:(NSObject *)scrollListener +{ + [_scrollListeners addObject:scrollListener]; +} + +- (void)removeScrollListener:(NSObject *)scrollListener +{ + if (scrollListener == _nativeScrollDelegate) { + [self setNativeScrollDelegate:nil]; + } else { + [_scrollListeners removeObject:scrollListener]; + } +} + - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [_scrollView dockClosestSectionHeader]; @@ -730,7 +762,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) // Fire a final scroll event _allowNextScrollNoMatterWhat = YES; [self scrollViewDidScroll:scrollView]; - + // Fire the end deceleration event RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil); RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndDecelerating:scrollView); @@ -741,16 +773,19 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll) // Fire a final scroll event _allowNextScrollNoMatterWhat = YES; [self scrollViewDidScroll:scrollView]; - + // Fire the end deceleration event - RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil); //TODO: shouldn't this be onScrollAnimationEnd? + RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil); //TODO: shouldn't this be onScrollAnimationEnd? RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndScrollingAnimation:scrollView); } - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { - if ([_nativeScrollDelegate respondsToSelector:_cmd]) { - return [_nativeScrollDelegate scrollViewShouldScrollToTop:scrollView]; + for (NSObject *scrollListener in _scrollListeners) { + if ([scrollListener respondsToSelector:_cmd] && + ![scrollListener scrollViewShouldScrollToTop:scrollView]) { + return NO; + } } return YES; } diff --git a/React/Views/RCTScrollableProtocol.h b/React/Views/RCTScrollableProtocol.h index f871d5192..617622173 100644 --- a/React/Views/RCTScrollableProtocol.h +++ b/React/Views/RCTScrollableProtocol.h @@ -15,11 +15,17 @@ */ @protocol RCTScrollableProtocol -@property (nonatomic, weak) NSObject *nativeScrollDelegate; +/* + * The nativeScrollDelegate property is now deprecated please use the scrollListener API instead + */ +@property (nonatomic, weak) NSObject *nativeScrollDelegate DEPRECATED_ATTRIBUTE; @property (nonatomic, readonly) CGSize contentSize; - (void)scrollToOffset:(CGPoint)offset; - (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated; - (void)zoomToRect:(CGRect)rect animated:(BOOL)animated; +- (void)addScrollListener:(NSObject *)scrollListener; +- (void)removeScrollListener:(NSObject *)scrollListener; + @end