Skip to content

iOS: footer's Yoga position doesn't match its native position — measure() is wrong and footer Pressables silently drop presses on real devices #726

Description

@joeltjames

Summary

On iOS (new architecture), the footer slot's shadow-tree (Yoga) position never matches its native on-screen position. styles.footer is position: 'absolute' with left/right but no top/bottom, so Yoga places the footer's shadow node at the container origin — while TrueSheetFooterView.mm pins the real view to the sheet bottom with AutoLayout (didMoveToSuperviewsetupConstraintsWithHeight:), and updateLayoutMetrics intentionally skips Yoga frames after the initial layout.

Since Fabric's measure()/measureInWindow() answer from the shadow tree, anything rendered in the footer measures at the wrong position. Captured on a physical iPhone where the footer visibly sits at y≈437:

measureInWindow → {x: 0, y: 0, width: 375, height: 95.7}   // shadow tree
touch on a footer button → pageY: 456.3                     // reality

User-visible consequence: footer buttons silently drop presses on real devices

React's Pressability (used by Pressable, TouchableOpacity, react-native-paper buttons, etc.) measures the pressed view on responder grant, and on every touchMove verifies the touch is still inside that rect. With the skewed rect, the first move event silently deactivates the press: onPressIn fires (so the button animates), but onPress never does.

This only reproduces on physical devices, and in our testing only on iOS 18 and below:

  • Stationary simulator taps emit zero touchMove events, so the check never runs.
  • Real fingers always jitter, but newer device/OS combos appear to coalesce micro-jitter moves away; older hardware (tested iPhone XS-class, iOS 16/18) reliably emits them — even zero-delta moves trigger the check.

The result is a ghost bug: footer buttons animate but do nothing, only on certain phones, and never in the simulator.

Reproduction

  • @lodev09/react-native-true-sheet 3.10.1 (the footer style is unchanged on main), React Native 0.83.6, new architecture, iOS.
  • Render any Pressable/Button in the footer slot, present the sheet, and tap the button on an older physical iPhone: press feedback fires, onPress doesn't. Logging measureInWindow on a footer wrapper vs. the touch's pageY shows the skew directly (see numbers above).

Possible fixes

Keep the footer's shadow position in sync with where the native side puts it — e.g. update the shadow node's layout when the footer is pinned, or lay the footer out in flow after the content instead of absolutely. Note that simply adding bottom: 0 to styles.footer is only correct when the container's Yoga height matches the native sheet height, which isn't guaranteed.

As a stopgap we're running a patch to React Native's Pressability that discards a measured press region that doesn't contain the touch that granted the responder (filed at facebook/react-native — link to follow in a comment).

Context

Found while working on the Planning Center mobile app after footer buttons stopped responding for users on older iPhones; diagnosed and fixed with the help of Claude (Fable). Happy to provide more logs or test candidate fixes on the affected hardware.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs reproNeed to replicate this issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions