[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:
parent
b57a14d07c
commit
66d3f3c616
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue