Home > Net >  Customise Section Headers ONLY when Headers are pinned, otherwise not (SwiftUI)
Customise Section Headers ONLY when Headers are pinned, otherwise not (SwiftUI)

Time:06-18

My question is simple: Is there any way to customise a Section's Header ONLY when it gets pinned, but keep it as it was before if not pinned?

I am trying to add a .shadow to a Section's Header and I don't want it to be visible all the time, but only when starting to scroll down past the Header in the parent ScrollView (when the header gets pinned).

I am mainly looking for a pure SwiftUI solution but I am also open to discuss other solutions. :)

struct WorkoutCardView: View {
    @Binding var workout: Workout
    @State var expandWorkout: Bool = false
    
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        LazyVStack(alignment: .leading, pinnedViews: .sectionHeaders) {
            Section {
                if expandWorkout {
                    WCExerciseSectionView(workout: $workout)
                }
            } header: {
                WCTitleSectionView(workout: $workout)
                    .background {
                        Color(uiColor: .systemBackground)
                        Color(uiColor: .systemFill)
                    }
                    .cornerRadius(10)
                    .shadow(color: colorScheme == .light ?
                            Color.black.opacity(expandWorkout ? 0.6 : 0) :
                                Color.white.opacity(expandWorkout ? 0.6 : 0), radius: 5, x: 0, y: 2)
                    .padding(.all, 2)
            }
        }
        .padding(.all, 8)
        .background {
            RoundedRectangle(cornerRadius: 10)
                .fill(Color(uiColor: .systemFill))
        }
        .padding(.all, 8)
        .onTapGesture {
            withAnimation {
                expandWorkout.toggle()
            }
        }
    }
}

Check current result here

CodePudding user response:

A possible approach is based on dynamic detection of header position relative to container coordinate space and if it is zero (i.e. pinned at top) then change style correspondingly.

Tested with Xcode 13.4 / iOS 15.5

demo

Here is main part:

                } header: {
                    HeaderView(value: "HEADER \(i   1)", pinned: i == pinned)
                        .background(GeometryReader {
                            // detect current position of header
                            Color.clear.preference(key: ViewOffsetKey.self,
                                value: $0.frame(in: .named("area")).origin.y)
                        })
                }
                .onPreferenceChange(ViewOffsetKey.self) {
                    // verify if position is zero (pinned) in container coordinates
                    if $0 == 0 {
                        self.pinned = i
                    } else if self.pinned == i {
                        // clean-up if changed (might depend - for simplicity)
                        self.pinned = nil
                    }
                }
            }
        }
    }.clipped()
}.coordinateSpace(name: "area")    // << here !!

Test module on GitHub

  • Related