import SwiftUI struct CategoryHeaderOffsetPreferenceKey: PreferenceKey { static let defaultValue: [String: CGFloat] = [:] static func reduce(value: inout [String: CGFloat], nextValue: () -> [String: CGFloat]) { value.merge(nextValue(), uniquingKeysWith: { _, new in new }) } } enum StoreDetailScrollCoordinateSpace { static let name = "store-detail-scroll" } struct ScrollOffsetPreferenceKey: PreferenceKey { static let defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } struct ScrollOffsetReader: View { @Binding var offsetY: CGFloat @State private var baseline: CGFloat? = nil var body: some View { Color.clear .frame(height: 0) .background( GeometryReader { geometry in Color.clear.preference( key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named(StoreDetailScrollCoordinateSpace.name)).minY ) } ) .onPreferenceChange(ScrollOffsetPreferenceKey.self) { minY in if baseline == nil { baseline = minY } let offset = (baseline ?? 0) - minY if abs(offsetY - offset) > 0.5 { offsetY = offset } } } } struct AsyncStoreImage: View { let imageURL: String? var fallbackImageName: String = "placeholder-product" var body: some View { CachedRemoteImage(imageURL: imageURL) { fallback } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(AppColors.brandSoft) .clipped() } private var fallback: some View { Image(fallbackImageName) .resizable() .scaledToFill() } }