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 (didMoveToSuperview → setupConstraintsWithHeight:), 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.
Summary
On iOS (new architecture), the
footerslot's shadow-tree (Yoga) position never matches its native on-screen position.styles.footerisposition: 'absolute'withleft/rightbut notop/bottom, so Yoga places the footer's shadow node at the container origin — whileTrueSheetFooterView.mmpins the real view to the sheet bottom with AutoLayout (didMoveToSuperview→setupConstraintsWithHeight:), andupdateLayoutMetricsintentionally 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:User-visible consequence: footer buttons silently drop presses on real devices
React's
Pressability(used byPressable,TouchableOpacity, react-native-paper buttons, etc.) measures the pressed view on responder grant, and on everytouchMoveverifies the touch is still inside that rect. With the skewed rect, the first move event silently deactivates the press:onPressInfires (so the button animates), butonPressnever does.This only reproduces on physical devices, and in our testing only on iOS 18 and below:
touchMoveevents, so the check never runs.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-sheet3.10.1 (the footer style is unchanged onmain), React Native 0.83.6, new architecture, iOS.Pressable/Buttonin thefooterslot, present the sheet, and tap the button on an older physical iPhone: press feedback fires,onPressdoesn't. LoggingmeasureInWindowon a footer wrapper vs. the touch'spageYshows 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: 0tostyles.footeris 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
Pressabilitythat 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.