当 ScrollView 向下滚动时隐藏 SwiftUI View,然后在向上滚动时再次显示它,即使没有一直滚动到顶部

问题描述 投票:0回答:1

我有一个带有 ScrollView 的 SwiftUI 视图,其构造如下:

ZStack(alignment: .top){
                Color.black
                
                VStack {
                    HeaderView()
                                            
                    ScrollView(.vertical) {
                        content
                    }
                }
             }

我想在用户在 ScrollView 上向下滚动时隐藏 HeaderView() ,但在用户向上滚动时显示它(最好有一点偏移),即使 ScrollView 没有滚动所有通往山顶的路。

这是在很多应用程序上完成的,包括 Artifact,它是这样做的:

使用 GeometryReadyer/ScrollViewReader 没有帮助/或者我没有正确实现它?

swiftui scroll scrollview offset scrollviewreader
1个回答
0
投票

如果您将

GeometryReader
放置在滚动内容的背景中,则可以使用它来检测滚动位置的变化。当滚动方向改变时,可以使用
.onChange
处理程序来切换标题的可见性。

但是,为了防止滚动弹跳导致标题可见性在不应该的情况下切换,可以使用另一个

GeometryReader
来测量
ScrollView
的高度。然后,可以将位置的测量限制为内容的精确高度,从而忽略反弹。

这是对您的示例的改编,以显示其工作原理:

@State private var showingHeader = true

var body: some View {
    VStack {
        if showingHeader {
            HeaderView()
                .transition(
                    .asymmetric(
                        insertion: .push(from: .top),
                        removal: .push(from: .bottom)
                    )
                )
        }
        GeometryReader { outer in
            let outerHeight = outer.size.height
            ScrollView(.vertical) {
                content
                    .background {
                        GeometryReader { proxy in
                            let contentHeight = proxy.size.height
                            let minY = max(
                                min(0, proxy.frame(in: .named("ScrollView")).minY),
                                -(contentHeight - outerHeight)
                            )
                            Rectangle()
                                .fill(.clear)
                                .onChange(of: minY) { oldVal, newVal in
                                    if (showingHeader && newVal < oldVal) || !showingHeader && newVal > oldVal {
                                        showingHeader.toggle()
                                    }
                                }
                        }
                    }
            }
            .coordinateSpace(name: "ScrollView")
        }
        // Prevent scrolling into the safe area
        .padding(.top, 1)
    }
    .background(.black)
    .animation(.easeInOut, value: showingHeader)
}

Animation

© www.soinside.com 2019 - 2024. All rights reserved.