Skip to content

fix(ios): ensure contentView forwards touches after glass effect setup#38

Merged
satya164 merged 1 commit intocallstack:mainfrom
Dhruv317:fix/content-view-touch-passthrough
Mar 27, 2026
Merged

fix(ios): ensure contentView forwards touches after glass effect setup#38
satya164 merged 1 commit intocallstack:mainfrom
Dhruv317:fix/content-view-touch-passthrough

Conversation

@Dhruv317
Copy link
Copy Markdown
Contributor

Problem

When LiquidGlassView is used in React Native (Fabric renderer) with interactive child components like Pressable, touches intermittently fail to reach those children. The bug is session-level: either all LiquidGlassView instances work for the entire app session, or none of them do. A cold restart may or may not fix it.

Root Cause

There is a race condition between Fabric's component mount pass and UIKit's layout pass.

LiquidGlassViewImpl.setupView() is called from layoutSubviews() and applies the UIGlassEffect via self.effect = glassEffect. When UIGlassEffect is applied, UIKit internally reconfigures the contentView (_UIVisualEffectContentView). If no subviews are present in contentView at that moment, the contentView can end up with userInteractionEnabled == false.

In Fabric, child component views are mounted into contentView via mountChildComponentView:index: — but this can happen after layoutSubviews has already triggered setupView(). The ordering depends on main thread scheduling and varies per app launch.

Once setupView() runs and the layoutSubviews guard (if self.effect != nil { return }) prevents it from re-running, the contentView touch state is locked for the lifetime of that view.

Timeline — broken session:

  1. layoutSubviewssetupView()self.effect = glassEffectcontentView.isUserInteractionEnabled set to false (no subviews yet)
  2. Fabric mounts Pressable children into contentView
  3. Children are visible but never receive touches

Timeline — working session:

  1. Fabric mounts Pressable children into contentView
  2. layoutSubviewssetupView()self.effect = glassEffectcontentView touch handling set up correctly (subviews present)
  3. Everything works

Fix

Explicitly set self.contentView.isUserInteractionEnabled = true after every effect application in setupView(). This guarantees touches always flow through to child views regardless of mount ordering.

Environment

  • iOS 26 beta (26.1)
  • React Native 0.83 (Fabric / New Architecture)
  • @callstack/liquid-glass 0.7.0

Reproduction

<LiquidGlassView effect="regular" style={{ borderRadius: 25, padding: 16 }}>
  <Pressable onPress={() => console.log('pressed')}>
    <Text>Tap me</Text>
  </Pressable>
</LiquidGlassView>

Cold-restart the app multiple times. On some launches, the Pressable will not respond to taps. Once it fails, it stays broken for the entire session. Once it works, it stays working.

When UIGlassEffect is applied to a UIVisualEffectView, the internal
contentView (_UIVisualEffectContentView) can be reconfigured with
userInteractionEnabled = false if no subviews are present at the time
the effect is set.

In React Native's Fabric renderer, child component views are mounted
into contentView via mountChildComponentView: — but this can happen
*after* layoutSubviews triggers setupView() and applies the glass
effect. The result is that contentView's touch handling is locked in a
"no children" state, and Pressable/Touchable children never receive
touch events for the lifetime of that view.

This is a race condition that depends on main thread scheduling between
Fabric's mount pass and UIKit's layout pass. It manifests as an
intermittent, session-level bug: either all LiquidGlassView instances
in a Fabric commit get working touch, or none of them do.

The fix: explicitly set contentView.isUserInteractionEnabled = true
after every effect application, guaranteeing touches always flow
through to child views regardless of mount ordering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hgaddipati1118
Copy link
Copy Markdown

Would love this to get merged is a pretty big bug for us :)

Copy link
Copy Markdown

@atlj atlj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't reproduce the issue with the given snippet, but I'm assuming you're seeing the issue on a real-world app with actual CPU load.
After seeing the docs for isUserInteractionEnabled, I'm thinking this is good to merge since we don't have any actual reason for this to be false.

Copy link
Copy Markdown
Member

@satya164 satya164 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR

@satya164 satya164 merged commit a8a5a19 into callstack:main Mar 27, 2026
3 of 5 checks passed
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.

5 participants