Rewrite detail as UIKit hybrid hero + promote to production
Created by: jedmund
Summary
The detail view is now a UIKit hybrid driven by a custom HeroTransitionAnimator instead of SwiftUI's matched-geometry / floating-image coordinator, and the HeroTest* harness it was built under has been promoted to the production detail path.
54 commits, +6,127 / −1,196. The short version of what happened across the branch:
-
Abandoned the pure-SwiftUI floating-image coordinator (
FloatingHeroCoordinator+CellFrameRegistry+ overlay-stack shims) — it had durability bugs that multiplied with nav depth. -
Tried iOS 26 native
NavigationTransition.zoom(sourceID:in:)— dropped it because a non-interactive spring dismiss couldn't match the pull-down feel we wanted. -
Landed on a UIKit hybrid:
PixillateNavigationControllerper tab,HeroTransitionAnimatortalks to anyHeroTransitionTargetconforming VC.CellRectRegistry+SwiftUIFeedScrollerhandle cell-rect lookup / scroll-to-cell at transition time. -
Grew a
HeroTest*harness under a dev flag so the animator could iterate on colored rects, then progressively onto real Pixiv data: images, bookmarks, share sheet, metadata card with tags / caption links (in-app Safari), lightbox with zoom transition, profile with real avatar/bio/counts, related-posts nested grid, multi-page pedestal with swipe-paging, adjacent-illust prefetch. -
Consolidated at the end: deleted the old
IllustDetailViewController/MasonryFeed/HomeView/RelatedIllustsSection/PedestalView/HeroTestHarnessRoot/ProfileDestination(and theAppSettings.Key.useHeroTestHarnessflag), renamedHeroTest*→Illust*to reclaim production names, and refactoredIllustFeedViewto be content-only (callers own the ScrollView + feed-scroller registration).
Notable additions
-
pixillate/UIKit/: new hybrid UIKit layer —PixillateNavigationController,HeroTransitionAnimator,HeroTransitionTarget,IllustDetailViewController,IllustProfileViewController,PagingPedestalView,NukeUIImageView,LightboxTransitionAnimator,CrossfadeSnapshotView,WindowChromeInsetting,TabBarVisibility. -
pixillate/Views/:IllustFeedView(Pinterest masonry),IllustFeedViewModel,CellRectRegistry,SwiftUIFeedScroller,MetadataView,IllustLightboxView,PixivImageSizeRegistry,PixivImageDiskCache,HeroImageStack. -
pixillate/Transitions/:SpringTuner+SpringTuningOverlayfor live spring tuning (surfaced in Settings → Developer). -
ImagePipeline.pixivgets a dedicated.pixivLightboxsibling with its own cache so full-resolution originals don't evict grid thumbnails; both use aTTLDataCachefor per-pipeline on-disk storage with age-based eviction.
Known trade-offs (tracked as follow-ups, not blockers)
-
Detail swipe-paging dead-ends at the source feed's tail. When a user swipes past the last item captured in the push payload, nothing happens — we should load-more through the source feed's view model and splice new items in. Deferred; saved as
project_harness_detail_endless_swipe.mdin agent memory. -
Multi-page cell tap flash under rapid taps — a mitigation shipped but wasn't re-verified post-refactor. Tracked as
project_multipage_flash_investigation.md. - Two
TODO(herotest)markers around estimated-offset fallback when a cell has never been mounted — a niche edge case that silently no-ops today.
Test plan
-
Launch signed-in → Home tab renders the following feed. -
Tap a cell → hero push to detail; pedestal, metadata card, tag pills, caption links (open in-app Safari), bookmark heart, lightbox via pedestal tap all work. -
Swipe left/right on the pedestal → feed advance; metadata card stays on screen (no flash), adjacent neighbors are pre-warmed. -
Tap user row in detail → profile pushes with real avatar, handle, bio, illust/manga counts. -
Tap a cell in that profile → another detail pushes; 5+ levels deep still pop back to the correct cell. -
Pull-to-dismiss from detail → animates back to the currently-displayed illust's cell (not just the initially-tapped one). -
Profile tab (your own): Works + Bookmarks grids render, tap → detail, back works. Bookmark-visibility menu still works. -
Tag feed (from a tag pill or from a search submit): renders, tap → detail, back works. Sort menu + time-range settings still apply. -
Lightbox: pinch-zoom, double-tap, seeded image appears instantly, close button adapts to image luminance. -
Rotate iPad → masonry column count re-buckets cleanly (no squish/stretch animation). -
grep -r "HeroTest\|useHeroTestHarness\|pushHeroTestDetail\|MasonryFeed\|HomeView\|RelatedIllustsSection"returns zero matches. -
Clean build from DerivedData.