gpt4 book ai didi

swift - 在 SwiftUI View 中创建一个拖动 handle (用于可拖动窗口等)

转载 作者:行者123 更新时间:2023-12-05 02:41:11 25 4
gpt4 key购买 nike

我正在尝试学习 SwiftUI,我有一个关于如何制作一个组件的问题,这个组件上有一个可以用来拖拽的句柄。网上有很多关于如何制作可拖动组件的教程,但没有一个能准确回答我的问题,所以我想我会向你们这些优秀的人寻求智慧。

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

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

即顶部红色部分为标题栏,蓝色区域为组件主体。现在,这个窗口 View 包含在另一个 View 中,您可以四处拖动它。按照我的阅读方式,您应该这样做(非常简单):

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
}
)
}
}
}

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

window being dragged around

但是,这不是我想要的:我不希望您只需在窗口内拖动就可以拖动组件,我只想通过拖动红色标题栏来拖动它。但是红色标题栏隐藏在 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
}
)
}
}
}

但我不知道您将如何以正确的方式实现 .titleBarGesture(或者这是否是正确的方式。手势是否应该作为 WindowView 构造函数的参数?)。任何人都可以帮助我,给我一些指示吗?

提前致谢!

最佳答案

您可以使用拖动的偏移量获得窗口的平滑平移,然后禁用背景元素上的触摸以防止内容被拖动。

按钮在内容区域仍然有效。

Dragable windows

import SwiftUI

struct WindowBar: View {
@Binding var location: CGPoint

var body: some View {
ZStack {
Color.red
.frame(height:25)
Text(String(format: "%.1f: %.1f", location.x, location.y))
}
}
}

struct WindowContent: View {
var body: some View {
ZStack {
Color.blue
.allowsHitTesting(false) // background prevents interaction
Button("Press Me") {
print("Tap")
}
}
}
}

struct WindowView: View, Identifiable {
@State var location: CGPoint // The views current center position
let id = UUID()

/// Keep track of total translation so that we don't jump on finger drag
/// SwiftUI doesn't have an onBegin callback like UIKit's gestures
@State private var startDragLocation = CGPoint.zero
@State private var isBeginDrag = true

init(location: CGPoint = .zero) {
_location = .init(initialValue: location)
}

var body: some View {
VStack(spacing:0) {
WindowBar(location: $location)
WindowContent()
}
.frame(width: 100, height: 100)
.position(location)
.gesture(DragGesture()
.onChanged({ value in
if isBeginDrag {
isBeginDrag = false
startDragLocation = location

}
// In UIKit we can reset translation to zero, but we can't in SwiftUI
// So we do book keeping to track startLocation of gesture and adjust by
// total translation
location = CGPoint(x: startDragLocation.x + value.translation.width,
y: startDragLocation.y + value.translation.height)
})
.onEnded({ value in
isBeginDrag = true /// reset for next drag
}))
}
}

struct ContainerView: View {

@State private var windows = [
WindowView(location: CGPoint(x: 50, y: 100)),
WindowView(location: CGPoint(x: 190, y: 75)),
WindowView(location: CGPoint(x: 250, y: 50))
]

var body: some View {
ZStack {
ForEach(windows) { window in
window
}
}
.frame(width: 600, height: 480)
}
}

struct ContentView: View {
var body: some View {
ContainerView()
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

关于swift - 在 SwiftUI View 中创建一个拖动 handle (用于可拖动窗口等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68212488/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com