Skip to content

Pull-to-dismiss: two-phase rubber-band drag with grid preview

jedmund requested to merge jedmund/sao-paulo-v8 into main

Created by: jedmund

Summary

Rewrite the detail VC's pull-to-dismiss as a direct-manipulation gesture so the user can drag the image around the grid before committing. Replaces the old UIPercentDrivenInteractiveTransition scrub (which locked the image to pan.y / 400) with a two-phase model:

  • Phase A (pan.y < 150pt): pedestal follows the finger 1:1, chrome stays up. Release → spring back.
  • Trigger (crossing 150pt): one-shot 0.26s spring scales the pedestal down to the source cell's width, fades all the chrome (close button, heart, metadata, below-pedestal stack, pedestal backdrop), drops a 28% black overlay behind the image, and drops a 24% / 12pt / 0,0 drop shadow on the image itself.
  • Phase B: pedestal pinned at grid-cell scale, rubber-bands in 2D around the trigger point. Asymptotic limits 1500pt Y / 1000pt X — near-1:1 in the normal drag range, bounded so the image can't fly off-screen.

Release past threshold (or >1200pt/s downward velocity) programmatically pops; the existing HeroTransitionAnimator reads pedestalRect(in:) (which respects the transform) and flies the snapshot from wherever the finger left it to the source cell. Release within the threshold → spring back.

Supporting plumbing:

  • Snapshot of the pushed-from VC's view captured at viewWillAppear and parked behind the detail content — in a standard UIKit push the previous VC's view is evicted from the window once the transition settles, so there's nothing to show through a transparent background. We ship our own copy.
  • systemBackground-filled placeholder overlays the backdrop at the source cell's rect so phase B reads as "image lifted from the grid."
  • Source cell rect captured at viewWillAppear (while the feed is still live and registered) and refreshed after each swipe-page — the LazyVStack un-registers cells during the hostview detach cycle around push, so without this both the scale calc and placeholder install silently fall back to defaults.

Test plan

  • Pull the detail down slightly, release within ~100pt → image returns to rest, chrome back in place.
  • Pull past ~150pt, image snaps to grid size, drop shadow + dim overlay appear, chrome fades.
  • In phase B, drag in both axes — image rubber-bands, asymptoting but never pinned.
  • Release past threshold → snapshot flies from wherever-the-finger-left-it to the actual grid cell; source cell re-appears.
  • Release back below threshold → spring back to rest, dim + shadow clear.
  • Swipe-page in the detail, then pull-dismiss → scale + placeholder + final fly-back all track the new current illust.
  • Fast flick downward → commits via the velocity threshold.

🤖 Generated with Claude Code

Merge request reports

Loading