[rn] Keep native ListView child frames in sync on JS wrapper

Summary: At the moment the `ListView.js` `_childFrames` variable is only updated on scroll. As a consequence, `onChangeVisibleRows` won't get triggered for the initial render, nor any future render not trigered by scroll events. To fix this we need to make sure native and JS have the child frames in sync.
This commit is contained in:
Martín Bigio 2015-07-07 07:38:24 -07:00
parent b57a14d07c
commit 66d3f3c616
3 changed files with 59 additions and 21 deletions

View File

@ -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 &&

View File

@ -357,6 +357,10 @@ RCT_NOT_IMPLEMENTED(-init)
@end
@interface RCTScrollView (Private)
- (NSArray *)calculateChildFramesData;
@end
@implementation RCTScrollView
{
RCTEventDispatcher *_eventDispatcher;
@ -533,6 +537,23 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
(_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 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
@"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

View File

@ -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 @@ RCT_EXPORT_METHOD(getContentSize:(NSNumber *)reactTag
}];
}
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