在 SwiftUI 视图内创建拖动手柄(用于可拖动窗口等)

Osk*_*kar 6 swift swiftui

我正在尝试学习 SwiftUI,并且有一个关于如何制作一个带有手柄的组件的问题,您可以用它来拖动。网上有几个关于如何制作可拖动组件的教程,但没有一个能够准确回答我的问题,所以我想我应该寻求你们这些好人的智慧。

假设您有一个类似于带有标题栏的窗口的视图。为了简单起见,让我们像这样:

struct WindowView: View {
    var body: some View {
        VStack(spacing:0) {
            Color.red
                .frame(height:25)
            Color.blue
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

即顶部红色部分为标题栏,蓝色区域为组件主体。现在,该窗口视图包含在另一个视图内,您可以将其拖动。根据我的阅读方式,你应该这样做(非常简单):

struct ContainerView: View {
    @State private var loc = CGPoint(x:150, y:150);
    
    var body: some View {
        ZStack {
            WindowView()
                .frame(width:100, height:100)
                .position(loc)
                .gesture(DragGesture()
                    .onChanged { value in
                        loc = value.location
                    }
                )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这确实可以让你拖动组件(暂时忽略,我们总是在图像的中心拖动,这不是重点):

窗口被拖动

然而,这不是我想要的:我不希望您能够通过在窗口内拖动来拖动组件,我只想通过拖动红色标题栏来拖动它。但红色标题栏隐藏在 WindowView 内部的某个位置。我不想将@State包含位置的变量移动到 WindowView 内部,在我看来,将其放在 ContainerView 内部更合乎逻辑。但随后我需要以某种方式将手势转发到嵌入的标题栏中。

我想最好的方法是 ContainerView 看起来像这样:

struct ContainerView: View {
    @State private var loc = CGPoint(x:150, y:150);

    var body: some View {
        ZStack {
            WindowView()
                .frame(width:100, height:100)
                .position(loc)
                .titleBarGesture(DragGesture()
                    .onChanged { value in
                        loc = value.location
                    }
                )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我不知道如何以.titleBarGesture正确的方式实现它(或者这是否是正确的方法。手势应该是 WindowView 构造函数的参数吗?)。谁能帮帮我,给我一些指示吗?

提前致谢!

dav*_*dev 1

您可以仅在蓝色视图上使用.allowsHitTesting(false),这将忽略该视图上的触摸手势。因此,您只能在红色视图上拖动,并且仍然可以在该视图之外使用 DragGesture。

struct WindowView: View {
    var body: some View {
        VStack(spacing:0) {
            Color.red
                .frame(height:25)
            Color.blue
                .allowsHitTesting(false)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)