How to collapse and Fade Header in SwiftUI - Stack Overflow

I am currently trying to implement a scroll to hide header. How can I shrink the height of the header a

I am currently trying to implement a scroll to hide header. How can I shrink the height of the header and fade it in as the user scrolls down. Then do the opposite when the user scrolls up(expand the header height to its initial value and fade it out). I have tried animating the opacity but it wasn't smooth. Here is the code for hiding and snapping the heading based on scroll direction.

   struct ScrollToHideView: View {
    
    @State private var naturalScrollOffset: CGFloat = 0
    @State private var lastNaturalOffset: CGFloat = 0
    @State private var headerOffset: CGFloat = 0
    @State private var isScrollingUp = false
    @State private var opacity = 1.0
    @State private var tempH = 0.0
    @State private var top = 0.0

    var body: some View {
        GeometryReader {
            let safeArea = $0.safeAreaInsets
            let headerHeight = 60.0 + safeArea.top
            ScrollView(.vertical) {
                LazyVStack {
                    ForEach(0..<10, id: \.self) { index in
                        DummyView()
                        
                    }
                }
                .padding(16)
            }
            .overlay(content: {
                 Text("\(naturalScrollOffset)")
            })
            .safeAreaInset(edge: .top, spacing: 0, content: {
                HeaderView()
                    .padding(.bottom, 16)
                    .frame(height: headerHeight, alignment: .bottom)
                    .background(.blue)
                    .opacity(opacity)
                    .offset(y: -headerOffset)
                    
            })
             
            .onScrollGeometryChange(for: CGFloat.self) { proxy in
                let maxHeight = proxy.contentSize.height - proxy.containerSize.height
                return max(min(proxy.contentOffset.y + headerHeight, maxHeight),0)
            } action: { oldValue, newValue in
                let isScrollingUp = oldValue < newValue
                headerOffset = min(max(newValue - lastNaturalOffset, 0), headerHeight)
                self.isScrollingUp = isScrollingUp
                // animating opacity
                withAnimation(.easeIn(duration: 2)) {
                    if self.isScrollingUp {
                        opacity = 0
                    } else {
                        opacity = 1
                    }
                }

                naturalScrollOffset = newValue
            }
            .onScrollPhaseChange({ oldPhase, newPhase, context in
                if !newPhase.isScrolling &&
                    (headerOffset != 0 || headerOffset != headerHeight) {
                    
                    withAnimation(.snappy(duration: 0.25, extraBounce: 0)) {
                        if headerOffset > (headerHeight * 0.5) &&
                            naturalScrollOffset > headerHeight {
                            headerOffset = headerHeight
                        } else {
                            headerOffset = 0
                        }
                        lastNaturalOffset = naturalScrollOffset - headerOffset
                    }
                    
                     
                    
                }
            })
            .onChange(of: isScrollingUp) { oldValue, newValue in
                lastNaturalOffset = naturalScrollOffset - headerOffset
                 
            }
            .ignoresSafeArea(.container, edges: .top)

        }
        
    }
}

 

extension ScrollToHideView {
    
    @ViewBuilder func HeaderView() -> some View {
        HStack(spacing: 20) {
            Image("user1")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(height: 25)
            
            Spacer(minLength: 0)
            
            Button("", systemImage: "airplayvideo") {
                
            }
            
            Button("", systemImage: "bell") {
                
            }
            
            Button("", systemImage: "magnifyingglass") {
                
            }
            
        }
        .font(.title2)
        .foregroundStyle(.primary)
        .padding(.horizontal, 16)
    }

    @ViewBuilder
    func DummyView() -> some View {
        VStack(alignment: .leading, spacing: 6) {
            RoundedRectangle(cornerRadius: 6)
                .frame(height: 220)
            
            HStack(spacing: 10) {
                Circle()
                    .frame(width: 45, height: 45)
                VStack(alignment: .leading, spacing: 4) {
                    Rectangle()
                        .frame(height: 10)
                    HStack {
                        Rectangle()
                            .frame(width: 100)
                        Rectangle()
                            .frame(width: 80)
                        Rectangle()
                            .frame(width: 80)
                        
                    }
                    .frame(height: 10.0)
                }
            }
        }
        .foregroundStyle(.tertiary)
    }
}

I am currently trying to implement a scroll to hide header. How can I shrink the height of the header and fade it in as the user scrolls down. Then do the opposite when the user scrolls up(expand the header height to its initial value and fade it out). I have tried animating the opacity but it wasn't smooth. Here is the code for hiding and snapping the heading based on scroll direction.

   struct ScrollToHideView: View {
    
    @State private var naturalScrollOffset: CGFloat = 0
    @State private var lastNaturalOffset: CGFloat = 0
    @State private var headerOffset: CGFloat = 0
    @State private var isScrollingUp = false
    @State private var opacity = 1.0
    @State private var tempH = 0.0
    @State private var top = 0.0

    var body: some View {
        GeometryReader {
            let safeArea = $0.safeAreaInsets
            let headerHeight = 60.0 + safeArea.top
            ScrollView(.vertical) {
                LazyVStack {
                    ForEach(0..<10, id: \.self) { index in
                        DummyView()
                        
                    }
                }
                .padding(16)
            }
            .overlay(content: {
                 Text("\(naturalScrollOffset)")
            })
            .safeAreaInset(edge: .top, spacing: 0, content: {
                HeaderView()
                    .padding(.bottom, 16)
                    .frame(height: headerHeight, alignment: .bottom)
                    .background(.blue)
                    .opacity(opacity)
                    .offset(y: -headerOffset)
                    
            })
             
            .onScrollGeometryChange(for: CGFloat.self) { proxy in
                let maxHeight = proxy.contentSize.height - proxy.containerSize.height
                return max(min(proxy.contentOffset.y + headerHeight, maxHeight),0)
            } action: { oldValue, newValue in
                let isScrollingUp = oldValue < newValue
                headerOffset = min(max(newValue - lastNaturalOffset, 0), headerHeight)
                self.isScrollingUp = isScrollingUp
                // animating opacity
                withAnimation(.easeIn(duration: 2)) {
                    if self.isScrollingUp {
                        opacity = 0
                    } else {
                        opacity = 1
                    }
                }

                naturalScrollOffset = newValue
            }
            .onScrollPhaseChange({ oldPhase, newPhase, context in
                if !newPhase.isScrolling &&
                    (headerOffset != 0 || headerOffset != headerHeight) {
                    
                    withAnimation(.snappy(duration: 0.25, extraBounce: 0)) {
                        if headerOffset > (headerHeight * 0.5) &&
                            naturalScrollOffset > headerHeight {
                            headerOffset = headerHeight
                        } else {
                            headerOffset = 0
                        }
                        lastNaturalOffset = naturalScrollOffset - headerOffset
                    }
                    
                     
                    
                }
            })
            .onChange(of: isScrollingUp) { oldValue, newValue in
                lastNaturalOffset = naturalScrollOffset - headerOffset
                 
            }
            .ignoresSafeArea(.container, edges: .top)

        }
        
    }
}

 

extension ScrollToHideView {
    
    @ViewBuilder func HeaderView() -> some View {
        HStack(spacing: 20) {
            Image("user1")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(height: 25)
            
            Spacer(minLength: 0)
            
            Button("", systemImage: "airplayvideo") {
                
            }
            
            Button("", systemImage: "bell") {
                
            }
            
            Button("", systemImage: "magnifyingglass") {
                
            }
            
        }
        .font(.title2)
        .foregroundStyle(.primary)
        .padding(.horizontal, 16)
    }

    @ViewBuilder
    func DummyView() -> some View {
        VStack(alignment: .leading, spacing: 6) {
            RoundedRectangle(cornerRadius: 6)
                .frame(height: 220)
            
            HStack(spacing: 10) {
                Circle()
                    .frame(width: 45, height: 45)
                VStack(alignment: .leading, spacing: 4) {
                    Rectangle()
                        .frame(height: 10)
                    HStack {
                        Rectangle()
                            .frame(width: 100)
                        Rectangle()
                            .frame(width: 80)
                        Rectangle()
                            .frame(width: 80)
                        
                    }
                    .frame(height: 10.0)
                }
            }
        }
        .foregroundStyle(.tertiary)
    }
}
Share Improve this question asked Mar 12 at 5:00 Jobie JJobie J 2672 silver badges9 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Instead of using a separate state variable for the opacity and animating this independently, you could make the opacity depend on the headerOffset:

// ScrollToHideView

// @State private var opacity = 1.0
.safeAreaInset(edge: .top, spacing: 0) {
    HeaderView()
        .padding(.bottom, 16)
        .frame(height: headerHeight, alignment: .bottom)
        .background(.blue)
        .opacity(1.0 - min(1.0, max(0.0, headerOffset / headerHeight))) // 

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744769792a4592688.html

相关推荐

  • How to collapse and Fade Header in SwiftUI - Stack Overflow

    I am currently trying to implement a scroll to hide header. How can I shrink the height of the header a

    20小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信