SwiftUI 拖动以关闭 ScrollView

rob*_*era 6 uiscrollview gesture ios swiftui

我想在 SwiftUI 中通过拖动来关闭滚动视图,如果您在内容顶部(偏移量 0)时继续拖动,它会关闭视图。

我正在努力在 SwiftUI 中实现这一点,但发现它相当困难。似乎我可以识别DragGesture或允许滚动,但不能同时识别两者。

我需要避免使用UIViewRepresentable并使用纯 SwiftUI 解决这个问题或尽可能接近。否则,它可能会使我的应用程序的其他部分的开发变得困难。

这是我遇到的问题的示例:

import SwiftUI

struct DragToDismissScrollView: View {
    enum SeenState {
        case collapsed
        case fullscreen
    }

    @GestureState var dragYOffset: CGFloat = 0
    @State var scrollYOffset: CGFloat = 0
    @State var seenState: SeenState = .collapsed

    var body: some View {
        GeometryReader { proxy in
            ZStack {
                Button {
                    seenState = .fullscreen
                } label: {
                    Text("Show ScrollView")
                }

                /*
                 * Works like a regular ScrollView but provides updates on the current yOffset of the content.
                 * Can find code for OffsetAwareScrollView in link below.
                 * Left out of question for brevity.
                 * https://gist.github.com/robhasacamera/9b0f3e06dcf27b54962ff0e077249e0d
                 */
                OffsetAwareScrollView { offset in
                    self.scrollYOffset = offset
                } content: {
                    ForEach(0 ... 100, id: \.self) { i in
                        Text("Item \(i)")
                            .frame(maxWidth: .infinity)
                    }
                }
                .background(Color.white)
                // If left at the default minimumDistance gesture isn't recognized
                .gesture(DragGesture(minimumDistance: 0)
                    .updating($dragYOffset) { value, gestureState, _ in
                        // Only want to start dismissing if at the top of the scrollview
                        guard scrollYOffset >= 0 else {
                            return
                        }

                        gestureState = value.translation.height
                    }
                    .onEnded { value in
                        if value.translation.height > proxy.frame(in: .local).size.height / 4 {
                            seenState = .collapsed
                        } else {
                            seenState = .fullscreen
                        }
                    })
                .offset(y: offsetForProxy(proxy))
                .animation(.spring())
            }
        }
    }

    func offsetForProxy(_ proxy: GeometryProxy) -> CGFloat {
        switch seenState {
        case .collapsed:
            return proxy.frame(in: .local).size.height
        case .fullscreen:
            return max(dragYOffset, 0)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:过去几天我尝试了很多解决方案(没有一个有效),包括:

  1. DragGesture使用此处提到的方法添加延迟: /sf/answers/4197337161/
  2. onTapGesture {}在此处提到的之前添加一个空调用DragGesture/sf/answers/4201057801/
  3. 删除手势并使用大于OffsetAwareScrollView0 时提供的偏移量。这不起作用,因为当 ScrollView 向下移动时,偏移量会随着OffsetAwareScrollView赶上内容而减小。