Pull-to-dismiss: two-phase rubber-band drag with grid preview
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
viewWillAppearand 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.