当 ScrollView 中的项目在屏幕上可见时,如何隐藏该项目?

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

当我在屏幕上看到第二个

BottomRoundedRectangle()
时,我试图隐藏
LazyVStack {…}
,但它不起作用。

value
中的
.onPreferenceChange(OffsetKey.self)
也没有改变。

struct OffsetKey: PreferenceKey {
    typealias Value = CGFloat

    static let defaultValue: Value = .zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}

struct OffsetKeyBackground: ViewModifier {
    func body(content: Content) -> some View {
        GeometryReader { reader in
            content
                .preference(
                    key: OffsetKey.self,
                    value: -reader.frame(in: .named("secondLazyStack")).origin.y
                )
        }
    }
}

struct ScrollQuestion: View {
    @State
    private var elementValue: CGFloat = .zero
    
    var body: some View {
        ScrollView(.vertical) {
            // Dynamic long text there
            LazyVStack {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.gray.opacity(0.3))
                    .frame(height: 920) // dynamic text, 920 for example, may be bigger
            }
            
            // 2nd LazyVStack
            LazyVStack {
                RoundedRectangle(cornerRadius: 12)
                    .frame(height: 820)
            }
            .coordinateSpace(name: "secondLazyStack")
        }
        .safeAreaInset(edge: .bottom) {
            // How to hide this correctly when we see second LazyVStack ?
            BottomRoundedRectangle()
//          .opacity(elementValue < 50 ? 1 : 0)
        }
        .modifier(OffsetKeyBackground())
        .onPreferenceChange(OffsetKey.self) { value in
            debugPrint(value)
            
            elementValue = value
        }
    }
}

struct BottomRoundedRectangle: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .frame(height: 40)
                .foregroundStyle(.indigo)
                .padding(.horizontal)
            
            Text("Visible only in first LazyVStack")
                .foregroundStyle(.white)
        }
    }
}

#Preview {
    ScrollQuestion()
}

我需要做什么才能正确隐藏

BottomRoundedRectangle()

swiftui scrollview geometryreader
1个回答
0
投票

您可以通过将第二个

LazyVStack
包裹在
GeometryReader
中来检测到这一点。

如果滚动视图的框架与

LazyVStack
的框架相交,则意味着
LazyVStack
的至少一部分位于屏幕上。

struct IsOnScreenKey: PreferenceKey {
    
    static let defaultValue = false
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}

struct DetectIsOnScreen: ViewModifier {
    func body(content: Content) -> some View {
        GeometryReader { reader in
            content
                .preference(
                    key: IsOnScreenKey.self,
                    // bounds(of: .scrollView) gives us the scroll view's frame
                    // frame(in: .local) gives us the LazyVStack's frame
                    // both are in the local coordinate space
                    value: reader.bounds(of: .scrollView)?.intersects(reader.frame(in: .local)) ?? false
                )
        }
    }
}

然后你可以这样做:

@State
private var isSecondVStackVisible = false

var body: some View {
    ScrollView(.vertical) {
        LazyVStack {
            RoundedRectangle(cornerRadius: 12)
                .fill(Color.gray.opacity(0.3))
                .frame(height: 920)
        }
        LazyVStack {
            RoundedRectangle(cornerRadius: 12)
                .modifier(DetectIsOnScreen())
                .frame(height: 820)
                .onPreferenceChange(IsOnScreenKey.self) { value in
                    isSecondVStackVisible = value
                }
        }
    }
    .safeAreaInset(edge: .bottom) {
        BottomRoundedRectangle()
                  .opacity(isSecondVStackVisible ? 0 : 1)
    }
}

请注意,在这种特殊情况下,也可以将首选项设置为

reader.bounds(of: .scrollView) != nil

这是因为

LazyVStack
很懒——如果它们不在屏幕上,它实际上就不存在。几何读取器无法将滚动视图的框架转换为不存在的坐标空间。

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