fix(store): replace ZStack overlay sticky approach with native pinnedViews + reliable scroll tracking

- Use LazyVStack(pinnedViews: [.sectionHeaders]) for native section header stickiness
- Move ScrollOffsetReader outside LazyVStack (sibling) to prevent lazy-unload breaking scroll tracking
- Add baseline capture to ScrollOffsetReader to correctly normalize offset regardless of ScrollView content insets
- Apply safeAreaTop padding to categoryTabs only when pinned (isCategoryTabsPinned), preventing oversized header before scroll
- Remove complex ZStack dual-tabs overlay, stickyProgress logic, and categoryTabsStickyProgress
- categoryTabs now accepts isPinned flag with easeInOut(0.15s) animation on pin transition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Daniel Arantes Loverde
2026-06-03 14:26:23 -03:00
parent b61f59c253
commit 69dd67b8a5
4 changed files with 37 additions and 65 deletions

View File

@@ -195,10 +195,15 @@ extension StoreDetailView {
}
}
func categoryTabs(proxy: ScrollViewProxy, topExtension: CGFloat = 0, stickyProgress: CGFloat = 0) -> some View {
let chipPaddingH: CGFloat = 10 + 6 * stickyProgress
let chipPaddingV: CGFloat = 4 + 5 * stickyProgress
let containerPaddingV: CGFloat = 2 + 8 * stickyProgress
func categoryTabs(proxy: ScrollViewProxy, isPinned: Bool = false) -> some View {
let safeTop: CGFloat = {
guard isPinned else { return 0 }
#if canImport(UIKit)
return UIDevice.appSafeAreaTop
#else
return 0
#endif
}()
return ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
@@ -217,8 +222,8 @@ extension StoreDetailView {
Text(category.name)
.font(AppTypography.heading3)
.foregroundStyle(active ? AppColors.textInverse : AppColors.textMuted)
.padding(.horizontal, chipPaddingH)
.padding(.vertical, chipPaddingV)
.padding(.horizontal, 16)
.padding(.vertical, 9)
.background(active ? AppColors.primary : AppColors.surface)
.clipShape(Capsule())
}
@@ -226,16 +231,11 @@ extension StoreDetailView {
}
}
.padding(.horizontal, 16)
.padding(.vertical, containerPaddingV)
.padding(.vertical, 10)
}
.padding(.top, safeTop)
.background(AppColors.backgroundLight)
.overlay(alignment: .top) {
AppColors.backgroundLight
.frame(height: topExtension)
.frame(maxWidth: .infinity)
.offset(y: -topExtension)
.allowsHitTesting(false)
}
.animation(.easeInOut(duration: 0.15), value: isPinned)
}
func productCard(_ item: StoreCatalogListItem, in category: StoreCatalogCategory) -> some View {