SwiftUI - 如何从外部 `simultaneousGesture` 中排除内部 `DragGesture`?
SwiftUI - how to exclude inner `DragGesture` from outer `simultaneousGesture`?
我在灰色容器内有一个黄色按钮和一个蓝色圆圈。我将 DragGesture
s 添加到圆圈和容器中 — 然而,圆圈永远不会移动。
这是我想要的行为:
Dragging the gray area
Dragging the button
Dragging the circle
The container moves
The container moves
The circle moves
这是我得到的:
Dragging the gray area
Dragging the button
Dragging the circle
The container moves ✅
The container moves ✅
The container moves ❌
这是我的代码:
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
}
)
}
}
如何阻止外圈 DragGesture
吞噬内圈的 DragGesture
?
一种方法是在 ContentView
中添加一个状态变量来跟踪圆圈的手势是否处于活动状态。当圆圈的手势处于活动状态时,在外部手势上使用 GestureMask
或 .subviews
以防止其激活。您可以使用 @State
:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
@State var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.onChanged { _ in
subviewDragIsActive = true
}
.onEnded { _ in
subviewDragIsActive = false
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
或者您可以使用 @GestureState
:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
@GestureState var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.updating($subviewDragIsActive) {
_, flag, _ in
flag = true
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
如果圆是单独定义的 View
类型,你应该使用 @State
这样你就可以传递 Binding
,像这样:
import SwiftUI
import PlaygroundSupport
struct CircleView: View {
@GestureState var circleOffset = CGSize.zero
@Binding var dragIsActive: Bool
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.onChanged { _ in dragIsActive = true }
.onEnded { _ in dragIsActive = false }
)
}
}
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@State var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
CircleView(dragIsActive: $subviewDragIsActive)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
如果多个子视图中的任何一个可能同时进行拖动(由于多点触控),您可能应该使用首选项系统将它们的 isActive 状态传递到 ContentView
,但这是一个复杂得多的实现.
我在灰色容器内有一个黄色按钮和一个蓝色圆圈。我将 DragGesture
s 添加到圆圈和容器中 — 然而,圆圈永远不会移动。
这是我想要的行为:
Dragging the gray area | Dragging the button | Dragging the circle |
---|---|---|
The container moves | The container moves | The circle moves |
这是我得到的:
Dragging the gray area | Dragging the button | Dragging the circle |
---|---|---|
The container moves ✅ |
The container moves ✅ |
The container moves ❌ |
这是我的代码:
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
}
)
}
}
如何阻止外圈 DragGesture
吞噬内圈的 DragGesture
?
一种方法是在 ContentView
中添加一个状态变量来跟踪圆圈的手势是否处于活动状态。当圆圈的手势处于活动状态时,在外部手势上使用 GestureMask
或 .subviews
以防止其激活。您可以使用 @State
:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
@State var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.onChanged { _ in
subviewDragIsActive = true
}
.onEnded { _ in
subviewDragIsActive = false
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
或者您可以使用 @GestureState
:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@GestureState var circleOffset = CGSize.zero
@GestureState var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.updating($subviewDragIsActive) {
_, flag, _ in
flag = true
}
)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
如果圆是单独定义的 View
类型,你应该使用 @State
这样你就可以传递 Binding
,像这样:
import SwiftUI
import PlaygroundSupport
struct CircleView: View {
@GestureState var circleOffset = CGSize.zero
@Binding var dragIsActive: Bool
var body: some View {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(circleOffset)
.gesture(
DragGesture() /// this never gets called!
.updating($circleOffset) { value, circleOffset, transaction in
circleOffset = value.translation
}
.onChanged { _ in dragIsActive = true }
.onEnded { _ in dragIsActive = false }
)
}
}
struct ContentView: View {
@GestureState var containerOffset = CGSize.zero
@State var subviewDragIsActive = false
var body: some View {
VStack { /// gray container
/// should move the gray container when dragged
Button {
print("Button Pressed")
} label: {
Text("Button")
.padding(50)
.background(Color.yellow)
}
/// should **NOT** move the gray container when dragged
/// How do I make it move the circle instead?
CircleView(dragIsActive: $subviewDragIsActive)
.padding(50)
}
.background(Color.gray)
.offset(containerOffset)
.simultaneousGesture( /// `simultaneous` is needed to allow dragging from the yellow button
DragGesture() /// move the gray container
.updating($containerOffset) { value, containerOffset, transaction in
containerOffset = value.translation
},
including: subviewDragIsActive ? .subviews : .all
)
}
}
PlaygroundPage.current.setLiveView(ContentView())
如果多个子视图中的任何一个可能同时进行拖动(由于多点触控),您可能应该使用首选项系统将它们的 isActive 状态传递到 ContentView
,但这是一个复杂得多的实现.