diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/LayoutEventsExample.js index 3407be48a8b4de..4dec90f8f3bc84 100644 --- a/Examples/UIExplorer/LayoutEventsExample.js +++ b/Examples/UIExplorer/LayoutEventsExample.js @@ -85,8 +85,7 @@ var LayoutEventExample = React.createClass({ return ( - onLayout events are called on mount and whenever layout is updated, - including after layout animations complete.{' '} + layout events are called on mount and whenever layout is recalculated. Note that the layout event will typically be received before the layout has updated on screen, especially when using layout animations.{' '} Press here to change layout. @@ -136,6 +135,9 @@ var styles = StyleSheet.create({ pressText: { fontWeight: 'bold', }, + italicText: { + fontStyle: 'italic', + }, }); exports.title = 'Layout Events'; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index fdb2ff6ed0c5b9..d5235b5be0b3fa 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -185,6 +185,10 @@ var View = React.createClass({ * Invoked on mount and layout changes with * * {nativeEvent: { layout: {x, y, width, height}}}. + * + * This event is fired immediately once the layout has been calculated, but + * the new layout may not yet be reflected on the screen at the time the + * event is received, especially if a layout animation is in progress. */ onLayout: PropTypes.func, diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 4969b0055ee13e..9acaff806ba233 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -448,29 +448,12 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *onLayoutEvents = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; for (RCTShadowView *shadowView in viewsWithNewFrames) { [frameReactTags addObject:shadowView.reactTag]; [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; [areNew addObject:@(shadowView.isNewView)]; [parentsAreNew addObject:@(shadowView.superview.isNewView)]; - - // TODO (#8214142): this can be greatly simplified by sending the layout - // event directly from the shadow thread, which may be better anyway. - id event = (id)kCFNull; - if (shadowView.onLayout) { - event = @{ - @"target": shadowView.reactTag, - @"layout": @{ - @"x": @(shadowView.frame.origin.x), - @"y": @(shadowView.frame.origin.y), - @"width": @(shadowView.frame.size.width), - @"height": @(shadowView.frame.size.height), - }, - }; - } - [onLayoutEvents addObject:event]; } for (RCTShadowView *shadowView in viewsWithNewFrames) { @@ -486,7 +469,20 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo for (RCTShadowView *shadowView in viewsWithNewFrames) { RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager]; RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView]; - if (block) [updateBlocks addObject:block]; + if (shadowView.onLayout) { + CGRect frame = shadowView.frame; + shadowView.onLayout(@{ + @"layout": @{ + @"x": @(frame.origin.x), + @"y": @(frame.origin.y), + @"width": @(frame.size.width), + @"height": @(frame.size.height), + }, + }); + } + if (block) { + [updateBlocks addObject:block]; + } } // Perform layout (possibly animated) @@ -497,7 +493,6 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo NSNumber *reactTag = frameReactTags[ii]; UIView *view = viewRegistry[reactTag]; CGRect frame = [frames[ii] CGRectValue]; - id event = onLayoutEvents[ii]; BOOL isNew = [areNew[ii] boolValue]; RCTAnimation *updateAnimation = isNew ? nil : _layoutAnimation.updateAnimation; @@ -506,9 +501,6 @@ - (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)roo void (^completion)(BOOL) = ^(BOOL finished) { completionsCalled++; - if (event != (id)kCFNull) { - [self.bridge.eventDispatcher sendInputEventWithName:@"layout" body:event]; - } if (callback && completionsCalled == frames.count - 1) { callback(@[@(finished)]); }