Skip to content

RCTShadowView API broken in latest version 0.58.6 - iOS only #23806

@windev92

Description

@windev92

Environment

React Native Environment Info:
System:
OS: macOS 10.14.3
CPU: (24) x64 Intel(R) Xeon(R) CPU E5-2697 v2 @ 2.70GHz
Memory: 43.30 GB / 64.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 10.15.0 - /usr/local/bin/node
npm: 6.4.1 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 11.2, macOS 10.13, tvOS 11.2, watchOS 4.2
Android SDK:
API Levels: 16, 21, 23, 24, 26, 27, 28
Build Tools: 23.0.1, 26.0.2, 26.0.3, 27.0.3, 28.0.3
System Images: android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom_64
IDEs:
Android Studio: 3.2 AI-181.5540.7.32.5056338
Xcode: 9.2/9C40b - /usr/bin/xcodebuild
npmPackages:
react: 16.6.3 => 16.6.3
react-native: 0.58.6 => 0.58.6
npmGlobalPackages:
react-native-cli: 2.0.1

Description

I am using the shadow view to locate the absolute coordinates of a component in the screen.
with 0.53.0 everything was fine, I was overriding applyLayoutNode and doing :

- (void)applyLayoutNode:(YGNodeRef)node
      viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
       absolutePosition:(CGPoint)absolutePosition
{
       [_bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
        RCTMyView *view = (RCTMyView*)viewRegistry[self.reactTag];
        if ([view isKindOfClass:[RCTMyView class]]) {
            CGRect rect = CGRectMake(absolutePosition.x+YGNodeLayoutGetLeft(node), absolutePosition.y+YGNodeLayoutGetTop(node), YGNodeLayoutGetWidth(node), YGNodeLayoutGetHeight(node));
            [view updateComponent:rect];
        } else {
            RCTLogError(@"Cannot update button: %@ (tag #%@) is not RCTMyView", view, self.reactTag);
        }
    }];    
    [super applyLayoutNode:node viewsWithNewFrame:viewsWithNewFrame 
 absolutePosition:absolutePosition];
}

This function was only invoked when the layout requested a change, e.g screen rotation

with the next version, the interface has been broken in iOS only(still the same in android), so my code didn't compile anymore, which is something odd, since it's a public interface and obliged me to change my component code depending on the version. RCTView interface is not broken, otherwise most 3rd party components will not compile anymore.

With 0.55.4, I need to overwrite layoutWithMetrics :

- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics
            layoutContext:(RCTLayoutContext)layoutContext {
    
    [_bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
        RCTMyView *view = (RCTMyView*)viewRegistry[self.reactTag];
        if ([view isKindOfClass:[RCTMyView class]]) {            
            // RN framework always adds 2 subviews to the rootView, the 3 anchored to the origin (0,0).
            // subv2 will be the parent of the view we declare in render()
            // --------------- rootView
            //    --------------- subv1
            //       --------------- subv2
            CGRect rect = layoutMetrics.frame;
            RCTShadowView* v = self.superview;
            while (v) {
                rect.origin.x += v.layoutMetrics.frame.origin.x;
                rect.origin.y += v.layoutMetrics.frame.origin.y;
                v = v.superview;
            }
            [view updateComponent:rect];
        } else {
            RCTLogError(@"Cannot update button: %@ (tag #%@) is not RCTMyView", view, self.reactTag);
        }
    }];
    
    [super layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
}

This new function has 3 problems:

  1. it is periodically invoked, even if nothing has changed on screen
  2. it is not possible to get the absolute coordinates using the parameters layoutMetrics or layoutContext, without having to loop as I do here. Remember in 0.53.0 I was having the absolute position of the parent node...
  3. is there any plan to break this public interface again in the future? this will not be a viable solution for my client who will need a new release everytime the interface is changed
    It would be nice to have a react native core developper having a look at this.

This shadow interface has not been broken anymore until 0.58.6, and its android equivalent has never been broken(LayoutShadowNode)

THE PROBLEM IS STILL VALID IN LATEST RN VERSION. THE DESCRIPTION OF THE ISSUE
CLEARLY SHOWS A REGRESSION IN IOS ONLY, WHILE ANDROID IS OK. PLEASE FORWARD TO AN IOS RN CORE DEVELOPPER TO ADDRESS IT. KINDLY NOT CLOSE THE ISSUE WITHOUT BEEING ADDRESSED.

Reproducible Demo

use latest RN and see the regression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions