diff --git a/Libraries/CustomComponents/ListView/ListView.js b/Libraries/CustomComponents/ListView/ListView.js index cebfac68e994e5..2cf967e5035ef1 100644 --- a/Libraries/CustomComponents/ListView/ListView.js +++ b/Libraries/CustomComponents/ListView/ListView.js @@ -29,6 +29,7 @@ var ListViewDataSource = require('ListViewDataSource'); var React = require('React'); var RCTUIManager = require('NativeModules').UIManager; +var RKScrollViewManager = require('NativeModules').ScrollViewManager; var ScrollView = require('ScrollView'); var ScrollResponder = require('ScrollResponder'); var StaticRenderer = require('StaticRenderer'); @@ -400,6 +401,13 @@ var ListView = React.createClass({ logError, this._setScrollVisibleHeight ); + + // RKScrollViewManager.calculateChildFrames not available on every platform + RKScrollViewManager && RKScrollViewManager.calculateChildFrames && + RKScrollViewManager.calculateChildFrames( + React.findNodeHandle(this.refs[SCROLLVIEW_REF]), + this._updateChildFrames, + ); }, _setScrollContentHeight: function(left, top, width, height) { @@ -412,6 +420,10 @@ var ListView = React.createClass({ this._renderMoreRowsIfNeeded(); }, + _updateChildFrames: function(childFrames) { + this._updateVisibleRows(childFrames); + }, + _renderMoreRowsIfNeeded: function() { if (this.scrollProperties.contentHeight === null || this.scrollProperties.visibleHeight === null || @@ -449,11 +461,10 @@ var ListView = React.createClass({ scrollProperties.offsetY; }, - _updateVisibleRows: function(e) { + _updateVisibleRows: function(updatedFrames) { if (!this.props.onChangeVisibleRows) { return; // No need to compute visible rows if there is no callback } - var updatedFrames = e && e.nativeEvent.updatedChildFrames; if (updatedFrames) { updatedFrames.forEach((newFrame) => { this._childFrames[newFrame.index] = merge(newFrame); @@ -522,7 +533,7 @@ var ListView = React.createClass({ this.scrollProperties.visibleHeight = e.nativeEvent.layoutMeasurement.height; this.scrollProperties.contentHeight = e.nativeEvent.contentSize.height; this.scrollProperties.offsetY = e.nativeEvent.contentOffset.y; - this._updateVisibleRows(e); + this._updateVisibleRows(e.nativeEvent.updatedChildFrames); var nearEnd = this._getDistanceFromEnd(this.scrollProperties) < this.props.onEndReachedThreshold; if (nearEnd && this.props.onEndReached && diff --git a/React/Views/RCTScrollView.m b/React/Views/RCTScrollView.m index 2528678493d56a..bc4db361ee9895 100644 --- a/React/Views/RCTScrollView.m +++ b/React/Views/RCTScrollView.m @@ -357,6 +357,10 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event @end +@interface RCTScrollView (Private) +- (NSArray *)calculateChildFramesData; +@end + @implementation RCTScrollView { RCTEventDispatcher *_eventDispatcher; @@ -533,6 +537,23 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView (_scrollEventThrottle > 0 && _scrollEventThrottle < (now - _lastScrollDispatchTime))) { // Calculate changed frames + NSArray *childFrames = [self calculateChildFramesData]; + + // Dispatch event + [_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeMove + reactTag:self.reactTag + scrollView:scrollView + userData:@{@"updatedChildFrames": childFrames}]; + + // Update dispatch time + _lastScrollDispatchTime = now; + _allowNextScrollNoMatterWhat = NO; + } + RCT_FORWARD_SCROLL_EVENT(scrollViewDidScroll:scrollView); +} + +- (NSArray *)calculateChildFramesData +{ NSMutableArray *updatedChildFrames = [[NSMutableArray alloc] init]; [[_contentView reactSubviews] enumerateObjectsUsingBlock: ^(UIView *subview, NSUInteger idx, __unused BOOL *stop) { @@ -558,26 +579,9 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView @"height": @(newFrame.size.height), }]; } - }]; - // If there are new frames, add them to event data - NSDictionary *userData = nil; - if (updatedChildFrames.count > 0) { - userData = @{@"updatedChildFrames": updatedChildFrames}; - } - - // Dispatch event - [_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeMove - reactTag:self.reactTag - scrollView:scrollView - userData:userData]; - - // Update dispatch time - _lastScrollDispatchTime = now; - _allowNextScrollNoMatterWhat = NO; - } - RCT_FORWARD_SCROLL_EVENT(scrollViewDidScroll:scrollView); + return updatedChildFrames; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView diff --git a/React/Views/RCTScrollViewManager.m b/React/Views/RCTScrollViewManager.m index 6e74d13332d7e6..a83bf61dae108c 100644 --- a/React/Views/RCTScrollViewManager.m +++ b/React/Views/RCTScrollViewManager.m @@ -14,6 +14,10 @@ #import "RCTSparseArray.h" #import "RCTUIManager.h" +@interface RCTScrollView (Private) +- (NSArray *)calculateChildFramesData; +@end + @implementation RCTConvert (UIScrollView) RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{ @@ -91,4 +95,23 @@ - (NSDictionary *)constantsToExport }]; } +RCT_EXPORT_METHOD(calculateChildFrames:(NSNumber *)reactTag + callback:(RCTResponseSenderBlock)callback) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + + UIView *view = viewRegistry[reactTag]; + if (!view) { + RCTLogError(@"Cannot find view with tag #%@", reactTag); + return; + } + + NSArray *childFrames = [((RCTScrollView *)view) calculateChildFramesData]; + + if (childFrames) { + callback(@[childFrames]); + } + }]; +} + @end