繁体   English   中英

SwiftUI - 如何从外部“simultaneousGesture”中排除内部“DragGesture”?

[英]SwiftUI - how to exclude inner `DragGesture` from outer `simultaneousGesture`?

我在灰色容器中有一个黄色按钮和一个蓝色圆圈。 我将DragGesture添加到圆圈和容器中——但是,圆圈永远不会移动。

这是我想要的行为:

拖动灰色区域 拖动按钮 拖动圆圈
容器移动 容器移动 圆圈移动

这就是我得到的:

拖动灰色区域 拖动按钮 拖动圆圈
容器移动✅
整个容器移动
容器移动✅
整个容器移动
容器移动❌
整个容器移动,但只有圆圈应该移动

这是我的代码:

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中添加一个 state 变量来跟踪圆圈的手势是否处于活动状态。 当圆圈的手势处于活动状态时,在外部手势上使用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 ,但这是一个复杂得多的实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM