swiftuiswiftui-scrollview

How can I read Drag location in ScrollView in SwiftUI?


I am trying to read location in DragGesture of a View connected to a ScrollView, but ScrollView take control of DragGesture on all view and does not let me to read those information. Here is my example to show the issue, My Goal is reading the Drag location in DragGesture.

using new code:

struct ContentView: View {
    @State var locationY: CGFloat = .zero
    
    var body: some View {
        VStack {                
            HStack {
                VStack {
                    Color.black.frame(height: 300)
                }
                .gesture( DragGesture().onChanged { value in locationY = value.location.y; print(locationY)} )
                

                ScrollView {
                    VStack {
                        Color.blue.frame(height: 300)
                    }.background(GeometryReader {
                        Color.clear.preference(key: ViewOffsetKey.self,
                                               value: $0.frame(in: .global).origin.y)
                        // value: $0.frame(in: .named("scroll_area")).origin.y)
                    })
                }
                // .coordinateSpace(name: "scroll_area")
                .onPreferenceChange(ViewOffsetKey.self) {
                    self.locationY = $0
                    print(">> offset: \($0)")
                }
                .frame(height: 300)
            }
            
            Spacer()
        }
    }
}
    
struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

Solution

  • Here is possible solution, to use GeometryReader in background to read rect of view (in any needed coordinate space) and view preferences to pass needed coordinate at top level.

    Tested with Xcode 12.1 / iOS 14.1

    Note: commented variant is for superview (ie. ScrollView) coordinates

        ScrollView
        {
            VStack
            {
                Color.blue.frame(height: 300)
                 }.background(GeometryReader {
                      Color.clear.preference(key: ViewOffsetKey.self,
                            value: $0.frame(in: .global).origin.y)
                            // value: $0.frame(in: .named("scroll_area")).origin.y)
                 })
        }
        // .coordinateSpace(name: "scroll_area")
             .onPreferenceChange(ViewOffsetKey.self) {
                self.locationY = $0
                print(">> offset: \($0)")
            }
    

    and helper preference key

    struct ViewOffsetKey: PreferenceKey {
        typealias Value = CGFloat
        static var defaultValue = CGFloat.zero
        static func reduce(value: inout Value, nextValue: () -> Value) {
            value += nextValue()
        }
    }