SwiftUI: DragGesture in ScrollView doesn't register scrolling when swipe begins on DragGesture view

Jor*_*tto 4 animation scrollview gesture swift swiftui

So I have this really nice animation I want when a user presses a view, where it will sort of "bounce" under their finger as if it reacted to the touch.

Initial Goal

I need to register when the user presses down on the view (black rectangle in this example), so I can start shrinking it, then I need to register when they release on the view, so I can allow it to return to it's original size. User needs to still be able to scroll normally in ScrollView

Initial "Solution"

I managed to achieve the desired animation using DragGesture() gesture with minimumDistance: 0.

See Example GIF here

This is achieved with the following code:

@State private var pressed: Bool = false

var body: some View {
        let drag = DragGesture(minimumDistance: 0)
            .onChanged({_ in
                self.pressed = true
            })
            .onEnded({_ in
                self.pressed = false
            })
        
        return ScrollView {
            Rectangle()
                .frame(minHeight: 100)
                .scaleEffect(self.pressed ? 0.5 : 1)
                .animation(.easeInOut)
//                .onTapGesture(perform: {
//                    self.pressed.toggle()
//                })
                .gesture(drag)
        }
    }
Run Code Online (Sandbox Code Playgroud)

Problem

If you user begins to scroll by starting with their finger on the rectangle, it will register as a drag gesture. If they begin scrolling elsewhere, scrolling is as normal

See Example GIF here

Alternative

If I use the modifier .onTapGesture() then I can tap and it's registered, and also begin scrolling and it registers as a scroll as appropriate

See Example GIF here

This is achieved with the following code:

@State private var pressed: Bool = false
    
    var body: some View {
        let drag = DragGesture(minimumDistance: 0)
            .onChanged({_ in
                self.pressed = true
            })
            .onEnded({_ in
                self.pressed = false
            })
        
        return ScrollView {
            Rectangle()
                .frame(minHeight: 100)
                .scaleEffect(self.pressed ? 0.5 : 1)
                .animation(.easeInOut)
                .onTapGesture(perform: {
                    self.pressed.toggle()
                })
//                .gesture(drag)
        }
    }
Run Code Online (Sandbox Code Playgroud)

Question

So how can I still get the desired animation whilst allowing scrolling? I've looked into GestureMasks, simultaneousGesture, LongPressGesture and more and they don't achieve the quick responsive animation I'm looking for...

Thanks!

Jor*_*tto 6

设法“大部分”自己修复它,发布答案以帮助其他人。

它并不完美,但好多了。如果有人能弄清楚如何使其在滚动时不按比例缩小,那就太好了。但除此之外,你可以点击它并滚动它就可以了。

struct ContentView: View {
    @State private var pressed: Bool = false

    var body: some View {
        print(self.pressed)
        return ScrollView {
            Button(action: { self.pressed.toggle() }) {
                Rectangle()
                    .frame(minHeight: 100)
                    .animation(.easeInOut)
            }.buttonStyle(tapBounceButtonStyle())
        }
    }
}

struct tapBounceButtonStyle: ButtonStyle {
  func makeBody(configuration: Self.Configuration) -> some View {
    configuration.label
        .scaleEffect(configuration.isPressed ? 0.9 : 1)
  }
}
Run Code Online (Sandbox Code Playgroud)