Skip to content

Fall back to opening the goal popover below, tracked via a scroll-safe coordinate space - #53

Draft
gsbernstein wants to merge 5 commits into
masterfrom
cursor/goal-popover-fallback-edge-2915
Draft

Fall back to opening the goal popover below, tracked via a scroll-safe coordinate space#53
gsbernstein wants to merge 5 commits into
masterfrom
cursor/goal-popover-fallback-edge-2915

Conversation

@gsbernstein

@gsbernstein gsbernstein commented Aug 14, 2026

Copy link
Copy Markdown
Owner

What changed

Re-implemented the "prefer above, fall back to below when there isn't room" behavior, this time using a technique that actually tracks the button's position correctly while scrolling.

Why the previous attempt failed

The earlier version used the common GeometryReader + .background + .global + PreferenceKey pattern to read the goal button's on-screen position. That pattern is well known to not reliably update during interactive ScrollView scrolling — .global frame values tend to only get recomputed when something else forces a re-render of that subtree, not simply because the user dragged the scroll view. In practice this meant the "space available above the button" was based on a stale position (most likely wherever the button was when the screen first appeared), so the fallback logic was comparing against the wrong numbers and would pick the wrong edge almost regardless of actual current scroll position.

The fix

  • ContentView's ScrollView now declares a named coordinate space (Constants.mainScrollCoordinateSpaceName) and publishes its own viewport size via the new scrollViewportSize environment value, measured with onGeometryChange (the ScrollView's own frame doesn't move as its content scrolls, so this value is stable/correct).
  • SleepBankCard's goal button reads its frame in: .named(mainScrollCoordinateSpaceName) via onGeometryChange, which — unlike .global, per the documented ScrollView "frame layer" technique — does update correctly and continuously as the user scrolls, since it's relative to the ScrollView's own fixed viewport rather than the whole screen.
  • goalPopoverArrowEdge compares the button's live position within that viewport against an estimated popover height to decide whether to open above (arrowEdge: .bottom, preferred) or below (arrowEdge: .top, fallback) — now driven by continuously-correct data instead of stale/guessed data.

Files changed

  • Bedtime/Bedtime/Constants.swift: new mainScrollCoordinateSpaceName.
  • Bedtime/Bedtime/ContentView.swift: declares the coordinate space on the main ScrollView and publishes its viewport size via environment.
  • Bedtime/Bedtime/Views/SleepBankCard.swift: reads the goal button's frame in that coordinate space and computes the arrow edge from it; preview updated to set up the same coordinate space/environment so it stays interactive.

Verification

This is a native iOS/SwiftUI change. Per the repo's AGENTS.md, this Cloud Agent environment runs Linux and cannot build/run Xcode or the iOS Simulator, so this has not been visually verified. Please confirm on device/Xcode that:

  • It still opens above in the common case.
  • Scrolling the card so the button is near the top of the screen and then tapping "Goal" now correctly falls back to opening below, and that this is correctly re-evaluated as you scroll (not just based on the initial position).
Open in Web Open in Cursor 

cursoragent and others added 2 commits August 14, 2026 05:47
…the screen

Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
…iOS 18+

Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
@cursor cursor Bot changed the title Fall back to opening the sleep goal popover downward near the top of the screen Anchor the goal popover to the button's corner with automatic edge fallback Aug 14, 2026
Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
@cursor cursor Bot changed the title Anchor the goal popover to the button's corner with automatic edge fallback Always open goal popover above, anchored to its trailing corner Aug 14, 2026
Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
@cursor cursor Bot changed the title Always open goal popover above, anchored to its trailing corner Always open the goal popover above the button Aug 14, 2026
The previous GeometryReader+.global+PreferenceKey approach didn't reliably
update during interactive scrolling, so the computed arrow edge was based
on a stale position (effectively wherever the button was when the view last
re-rendered, not its actual current on-screen position). Using a named
coordinate space anchored to the main ScrollView's own (non-scrolling)
viewport, read via onGeometryChange, tracks the button's position within
that viewport correctly and continuously as the user scrolls.

Co-authored-by: Greg <gsbernstein@users.noreply.github.com>
@cursor cursor Bot changed the title Always open the goal popover above the button Fall back to opening the goal popover below, tracked via a scroll-safe coordinate space Aug 14, 2026
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