Skip to content

fix: scrolling gets stuck on elements with press-* feedback (iOS 18) - #394

Open
SRWieZ wants to merge 1 commit into
NativePHP:mainfrom
SRWieZ:fix/ios-press-feedback-scroll
Open

fix: scrolling gets stuck on elements with press-* feedback (iOS 18)#394
SRWieZ wants to merge 1 commit into
NativePHP:mainfrom
SRWieZ:fix/ios-press-feedback-scroll

Conversation

@SRWieZ

@SRWieZ SRWieZ commented Aug 26, 2026

Copy link
Copy Markdown
Member

Replacement for #374, which was accidentally merged and then reverted. Reopened from the original branch at the maintainer’s request.

What's wrong

On iOS 18, a scroll that starts on any element with press-scale (or any press-* prop) never scrolls. The press feedback watched for touch contact with a zero-distance DragGesture attached via simultaneousGesture; on iOS 18 that drag claims the touch before the surrounding ScrollView can begin its pan. On a screen made mostly of pressable cards, the only way to scroll is to aim for the gaps between them.

Minimum repro (fill a screen with these — swipes that start on a card don't scroll):

<native:scroll-view class="w-full flex-1">
    <native:column class="w-full p-5 gap-3">
        @for ($i = 0; $i < 20; $i++)
            <native:pressable :press-scale="0.97" class="w-full h-[80] rounded-2xl bg-white items-center justify-center">
                <native:text font="semibold" class="font-semibold">Card {{ $i }}</native:text>
            </native:pressable>
        @endfor
    </native:column>
</native:scroll-view>

What this does

  • Swaps the drag for _onButtonGesture(pressing:) — the recognizer SwiftUI's own Button uses. A ScrollView already knows how to share touches with buttons: pans win, taps still land, and the pressed effect engages only once the system resolves the touch as a press.
  • @tap is untouched (it fires through its own handler). Android is untouched.

No screenshot — the bug is a gesture, not pixels; the repro above shows it in seconds on an iOS 18 device.

…scrolling

The press-* feedback modifier detected touch contact with a zero-distance
DragGesture attached via simultaneousGesture. On iOS 18 that drag claims
the touch before an enclosing ScrollView can begin its pan, so a scroll
that starts on any element with press feedback never starts — screens
whose content is mostly pressable cards were only scrollable from the
gaps between them.

Use _onButtonGesture(pressing:) instead — the recognizer Button itself
uses. ScrollView cooperates with it exactly like it does with buttons:
pans win, taps still land, and the pressed highlight engages only when
the system resolves the touch as a press. The @tap handler is untouched;
perform is deliberately empty.

@shanerbaner82 shanerbaner82 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review notes (Air / sim pass)

Diff: One Swift file. Replaces zero-distance DragGesture + simultaneousGesture with _onButtonGesture(pressing:) so ScrollView can share the touch the way a Button does. @tap path left alone; Android untouched. Diagnosis matches the known iOS 18 pressable-vs-scroll failure mode.

Caveat: _onButtonGesture is an underscore SwiftUI API (same family as what Button uses). Fine if we accept that risk, but worth an explicit maintainer call.

Sim demo (iPhone 16 / iOS 18.4): Built the PR repro (dense press-scale cards in a scroll view) on main and on this branch.

I could not reproduce a stuck scroll via Maestro/XCUITest: swipes that start on cards scrolled on both main and this PR. So these clips are not a clean stuck-vs-fixed pair. Finger contact on a real device (or a gesture path that engages the press recognizer) is still the right confirmation.

Media (Drive / Plugins / PRs):

Verdict from code + prior #374 context: Looks right. Not approving from here — want a finger check on device (or your call on the private API).

— Air (drafted as shanerbaner82)

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.

2 participants