5

I'm trying to build a view where the header is fixed at the top of the view and it changes it's size according to the scroll offset, when the offset is 0 the header is bigger and when the user scrolls the header becomes smaller

struct ContentView : View {
    
    @State var largeHeader = true
    
    var body: some View {
        VStack {
            Text("HEADER").padding(.vertical, largeHeader ? 30 : 10)
            Divider()
            ScrollView {
                VStack {
                    Text("Content0")
                        .padding()
                    Text("Content1")
                        .padding()
                    Text("Content2")
                        .padding()
                }
                .background(GeometryReader { geometryProxy -> Color in
                    DispatchQueue.main.async {
                        largeHeader = geometryProxy.frame(in: .named("myspace")).minY >= 0
                    }
                    return Color.clear
                })
            }
            .coordinateSpace(name: "myspace")
        }.animation(.default)
    }
}

It works fine when the scroll content is longer, but when there is a little content, as in the code above I get this flickering (It's even worse on the device, but the gif quality is low)

enter image description here

Any idea how to fix it?

1 Answer 1

8

Looks like there is some interference going on.

I have found two workaround-solutions, it depends on what your desired effect is.

Solution 1

Idea: Using ZStack so that ScrollView and your header don't interfere.

struct ContentView: View {
    
    @State var largeHeader = true
    
    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach(0..<3) { i in
                        Text("Content\(i)")
                            .padding()
                    }
                }
                .background(GeometryReader { geometryProxy -> Color in
                    DispatchQueue.main.async {
                        largeHeader = geometryProxy.frame(in: .named("1")).minY >= 0
                    }
                    return Color.clear
                })
            }
            .coordinateSpace(name: "1")
            .offset(y: largeHeader ? 100 : 60)
            
            VStack {
                VStack {
                    Spacer()
                    Text("HEADER")
                        .padding()
                    Divider()
                }
                .frame(maxWidth: .infinity)
                .frame(height: largeHeader ? 140 : 100)
                .background(Color.white)
                
                Spacer()
            }
            .edgesIgnoringSafeArea(.all)
        }
        .animation(.default)
    }
}

The header of this one would always change the height, no matter how large the content-height is.

Solution 2

Idea: Only change header height when there is enough content to scroll.

Solution: Finding out the height of the scrollview-content.

struct ContentView: View {
    
    @State var largeHeader = true
    @State var scrollViewScrollable = false
    
    var body: some View {
        VStack {
            Text("HEADER").padding(.vertical, largeHeader ? 30 : 10)
            Divider()
            ScrollView {
                VStack {
                    ForEach(0..<3) { i in
                        Text("Content\(i)")
                            .padding()
                    }
                }
                .background(GeometryReader { geometryProxy -> Color in
                    if scrollViewScrollable {
                        DispatchQueue.main.async {
                            largeHeader = geometryProxy.frame(in: .named("1")).minY >= 0
                        }
                    }
                    return Color.clear
                })
                .background(
                    GeometryReader { proxy in
                        Color.clear.onAppear {
                            scrollViewScrollable = proxy.frame(in: .named("1")).size.height >= UIScreen.main.bounds.size.height - 100
                        }
                    }
                )
            }
            .coordinateSpace(name: "1")
        }
        .animation(.default)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.