Skip to content

fix: footer touch swallowing and safe area inset bugs#589

Open
isaacrowntree wants to merge 1 commit intolodev09:mainfrom
isaacrowntree:fix/footer-touch-and-safe-area
Open

fix: footer touch swallowing and safe area inset bugs#589
isaacrowntree wants to merge 1 commit intolodev09:mainfrom
isaacrowntree:fix/footer-touch-and-safe-area

Conversation

@isaacrowntree
Copy link
Copy Markdown
Contributor

Summary

  • iOS: Footer buttons/touchables don't respond to taps because Fabric's touch system hit-tests using stale Yoga frames while AutoLayout has moved the footer to a different position. Fix: give the footer its own RCTSurfaceTouchHandler so it has an independent coordinate space rooted at its actual frame.
  • Android: Footer touches dispatched to JS twice — once by dispatchTouchEvent routing to footer's RootView, and again by onInterceptTouchEvent/onTouchEvent on the ViewController. Fix: skip JS dispatch in the ViewController when the touch is in footer bounds.
  • iOS: Footer ignores safe area insets — content renders behind the home indicator on notched iPhones. Fix: pin to safeAreaLayoutGuide.bottomAnchor instead of bottomAnchor.
  • Android: Footer renders behind the navigation/gesture bar. Fix: subtract bottomInset in positionFooter (skipped when keyboard is visible since currentKeyboardInset already accounts for it).

Root cause (iOS touch bug)

TrueSheetFooterView.updateLayoutMetrics skips [super updateLayoutMetrics:...] after initial layout (line 93-96), so the Yoga frame goes stale while AutoLayout moves the view to the container bottom. RCTSurfaceTouchHandler on the container hit-tests using Yoga frames → can't find the footer at its visual position → touches dropped or delivered to the wrong component.

The fix mirrors the existing Android pattern where TrueSheetFooterView implements RootView with its own JSTouchDispatcher — on iOS we achieve the same by giving the footer its own RCTSurfaceTouchHandler.

Test plan

  • Tap Button and TouchableOpacity in footer on physical iOS device — taps register
  • Tap footer buttons on physical Android device — taps register, no double-press
  • Footer content doesn't render behind home indicator (notched iPhone)
  • Footer content doesn't render behind gesture bar (Android)
  • Keyboard avoidance still works — footer moves up with keyboard
  • Test with dismissible={false}
  • Test at different detent sizes (small, medium, full)
  • Test with and without insetAdjustment="automatic"

Reproduction app: https://github.com/isaacrowntree/truesheet-touch-repro

🤖 Generated with Claude Code

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 4, 2026

@isaacrowntree is attempting to deploy a commit to the Jovanni's projects Team on Vercel.

A member of the Team first needs to authorize it.

@lodev09
Copy link
Copy Markdown
Owner

lodev09 commented Mar 4, 2026

Thanks @isaacrowntree!

Do you have a repro from a fork of this repo that I can replicate on my end prior to your changes? I want to test this properly.

isaacrowntree added a commit to isaacrowntree/truesheet-touch-repro that referenced this pull request Mar 10, 2026
Reproduces two FooterComponent bugs:
1. Footer touches dropped on iOS, doubled on Android
2. Footer renders behind safe area insets on both platforms

Includes tap log to detect double-fire on Android and a
control button inside the sheet body for comparison.

Related: lodev09/react-native-true-sheet#589

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
iOS: Give footer its own RCTSurfaceTouchHandler so touches are hit-tested
against the footer's actual AutoLayout frame instead of the stale Yoga
frame in the container's touch handler. Pin footer bottom constraint to
safeAreaLayoutGuide.bottomAnchor so content doesn't render behind the
home indicator on notched devices.

Android: Skip JS touch dispatch in ViewController's onInterceptTouchEvent
and onTouchEvent when the touch lands in footer bounds, preventing double
dispatch (footer's own RootView handles it). Subtract bottomInset in
positionFooter so footer content doesn't render behind the gesture bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@isaacrowntree isaacrowntree force-pushed the fix/footer-touch-and-safe-area branch from d8f9c97 to 36e4804 Compare March 10, 2026 00:41
isaacrowntree added a commit to isaacrowntree/react-native-true-sheet that referenced this pull request Mar 10, 2026
Makes BasicSheet's footer tappable with an Alert so the touch
swallowing bug is immediately visible on physical devices.

iOS: taps on the footer are silently dropped
Android: taps fire twice (double alert)

Related: lodev09#589

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@isaacrowntree
Copy link
Copy Markdown
Contributor Author

isaacrowntree commented Mar 10, 2026

Hey @lodev09! Here's a repro branch on my fork of this repo:

isaacrowntree/react-native-true-sheet@repro/footer-touch

It adds a FooterReproSheet to the Test Screen in the example app with three variants covering all the test cases:

Button What it tests
Footer Repro Tappable button + TextInput in footer (touch bug + keyboard avoidance)
Footer (dismissible=false) Same footer with dismissible={false}
Footer (insetAdjustment=auto) Same footer with insetAdjustment="automatic"

All three use detents={['auto', 'medium', 'large']} so you can test at different sizes.

On a physical device before the fix:

  • iOS: footer button taps are silently dropped, TextInput can't be focused
  • Android: footer button taps fire twice (double alert)
  • Both: footer renders behind home indicator / gesture bar on notched devices

The existing BasicSheet footer is also made tappable for a quick sanity check.

I also rebased this PR on latest main so the conflicts are resolved.

@lowbits
Copy link
Copy Markdown

lowbits commented Mar 12, 2026

facing the same issue on iPhone Xs

@lodev09
Copy link
Copy Markdown
Owner

lodev09 commented Mar 27, 2026

@isaacrowntree I tested this with your provided repro and I couldn't replicate. what device are you testing this with? Also the footer is crashing

isaacrowntree added a commit to isaacrowntree/react-native-true-sheet that referenced this pull request Mar 28, 2026
Makes BasicSheet's footer tappable with an Alert so the touch
swallowing bug is immediately visible on physical devices.

iOS: taps on the footer are silently dropped
Android: taps fire twice (double alert)

Related: lodev09#589

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@isaacrowntree
Copy link
Copy Markdown
Contributor Author

Hey @lodev09! I've updated the repro branch and also simplified it to make reproduction much easier.

Updated repro branch

isaacrowntree/react-native-true-sheet@repro/footer-touch

Changes:

  • Rebased onto latest main (3.10.0-beta.3) — resolves any stale native code issues
  • Bumped RN 0.82 → 0.84.1 + reanimated 4.3, worklets 0.8.1, screens 4.24 — builds cleanly on Xcode 26
  • Fixed TS errors from beta.3 type changes (SheetDetent, InsetAdjustment, reanimated return types)
  • Replaced the example app with a single-screen repro that auto-presents the sheet — no navigation needed

How to test

git clone https://github.com/isaacrowntree/react-native-true-sheet.git --branch repro/footer-touch
cd react-native-true-sheet
yarn install
cd example/bare/ios && pod install && cd ../../..
# Update DEVELOPMENT_TEAM in Xcode to your signing team
# Build and run on a physical iOS device

The app auto-presents a sheet with two tappable buttons:

Button Location Expected
CONTENT: 0/10 Sheet body (control) Always registers — should reach 10/10
FOOTER: 0/10 Footer Bug: every 2nd tap is silently swallowed

Both buttons show a counter out of 10, turn red while counting and green at 10/10. The footer button also fires an Alert on each tap so you can clearly see which taps register and which are dropped.

What I observed

  • Device: iPhone 15 Pro, iOS 18
  • Content button: 10 taps → 10/10 ✅ every time
  • Footer button: 10 taps → ~5/10 ❌ — every 2nd tap is swallowed
  • The pattern is very consistent: tap-register-tap-drop-tap-register-tap-drop

Re: the crash you mentioned

Could you share the crash log / stack trace? With the updated branch (rebased on beta.3, RN 0.84) the crash may be resolved. The previous branch was pinned to beta.2 which could have had stale native state if your local pods came from a newer version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants