Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
337 views
in Technique[技术] by (71.8m points)

ios - Animate header when scroll in swiftui

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?

question from:https://stackoverflow.com/questions/65941330/animate-header-when-scroll-in-swiftui

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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)
    }
}
image image

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)
    }
}
image image

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...