Adding a drag gesture in SwiftUI to a View inside

2020-02-23 08:13发布

So I have a ScrollView holding a set of views:

    ScrollView {
        ForEach(cities) { city in
            NavigationLink(destination: ...) {
                CityRow(city: city)
            }
            .buttonStyle(BackgroundButtonStyle())
        }
    }

In every view I have a drag gesture:

    let drag = DragGesture()
        .updating($gestureState) { value, gestureState, _ in
            // ...
        }
        .onEnded { value in
            // ...
        }

Which I assign to a part of the view:

    ZStack(alignment: .leading) {
        HStack {
            // ...
        }
        HStack {
            // ...
        }
        .gesture(drag)
    }

As soon as I attach the gesture, the ScrollView stop scrolling. The only way to make it scroll it to start scrolling from a part of it which has no gesture attached. How can I avoid it and make both work together. In UIKit is was as simple as specifying true in shouldRecognizeSimultaneouslyWith method. How can I have the same in SwiftUI?

In SwiftUI I've tried attaching a gesture using .simultaneousGesture(drag) and .highPriorityGesture(drag) – they all work the same as .gesture(drag). I've also tried providing all possible static GestureMask values for including: parameter – I have either scroll working or my drag gesture working. Never both of them.

Here's what I'm using drag gesture for: enter image description here

标签: swiftui ios13
8条回答
混吃等死
2楼-- · 2020-02-23 08:22

Just before

.gesture(drag)

You can add

.onTapGesture { }

This works for me, apparently adding a tapGesture avoids confusion between the two DragGestures.

I hope this helps

查看更多
Juvenile、少年°
3楼-- · 2020-02-23 08:29

I had a similar problem with dragging a slider at:

stackoverflow question

This is the working answer code, with the "trick" of the "DispatchQueue.main.asyncAfter"

Maybe you could try something similar for your ScrollView.

struct ContentView: View {
@State var pos = CGSize.zero
@State var prev = CGSize.zero
@State var value = 0.0
@State var flag = true

var body: some View {
    let drag = DragGesture()
        .onChanged { value in
            self.pos = CGSize(width: value.translation.width + self.prev.width, height: value.translation.height + self.prev.height)
    }
    .onEnded { value in
        self.pos = CGSize(width: value.translation.width + self.prev.width, height: value.translation.height + self.prev.height)
        self.prev = self.pos
    }
    return VStack {
        Slider(value: $value, in: 0...100, step: 1) { _ in
            self.flag = false
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                self.flag = true
            }
        }
    }
    .frame(width: 250, height: 40, alignment: .center)
    .overlay(RoundedRectangle(cornerRadius: 25).stroke(lineWidth: 2).foregroundColor(Color.black))
    .offset(x: self.pos.width, y: self.pos.height)
    .gesture(flag ? drag : nil)
}

}

查看更多
霸刀☆藐视天下
4楼-- · 2020-02-23 08:39

I believe what you want is simultaneousGesture(_:including:), which is documented here.

HStack {
    // ...
}
.simultaneousGesture(drag)
查看更多
Root(大扎)
5楼-- · 2020-02-23 08:40

You can set minimumDistance to some value (for instance 30). Then the drag only works when you drag horizontally and reach the minimum distance, otherwise the scrollview or list gesture override the view gesture

.gesture(DragGesture(minimumDistance: 30, coordinateSpace: .local)
查看更多
Anthone
6楼-- · 2020-02-23 08:43

I attempted to implement a similar list style in my app only to find that the gestures conflicted with the ScrollView. After having spent hours researching and attempting possible fixes and workarounds for this issue, as of XCode 11.3.1, I believe this to be a bug that Apple needs to resolve in future versions of SwiftUI.

A Github repo with sample code to replicate the issue has been put together here and has been reported to Apple with the reference FB7518403.

Here's hoping it is fixed soon!

查看更多
做个烂人
7楼-- · 2020-02-23 08:46

I have created an easy to use extension based on the Michel's answer.

struct NoButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
    }
}

extension View {
    func delayTouches() -> some View {
        Button(action: {}) {
            highPriorityGesture(TapGesture())
        }
        .buttonStyle(NoButtonStyle())
    }
}

You apply it after using a drag gesture.

Example:

ScrollView {
    YourView()
        .gesture(DragGesture(minimumDistance: 0)
            .onChanged { _ in }
            .onEnded { _ in }
        )
        .delayTouches()
}
查看更多
登录 后发表回答