SwiftUI拖动操作DragGesture
SwiftUI拖动操作DragGesture

SwiftUI拖动操作DragGesture

在 SwiftUI 中,DragGesture 是一种手势,用于检测用户在屏幕上的拖动操作。它可以帮助实现视图的拖拽、滑动和移动等交互行为。

基本语法

DragGesture(minimumDistance: CGFloat = 0, coordinateSpace: CoordinateSpace = .local)

参数

minimumDistance:手势开始的最小拖动距离(默认值为 0)。用户必须拖动至少这么多,手势才会生效。

coordinateSpace:指定手势的坐标空间(.local 是默认值,表示视图的本地坐标)。

参数解析

.gesture(DragGesture()) 是 SwiftUI 中用于检测拖动手势的一个修饰符。它通过 DragGesture() 来捕获用户的拖动行为,并通过 onChanged 和 onEnded 方法来处理手势的不同阶段。

.gesture(
    DragGesture()
        .onChanged { value in
            // 手势正在进行时,每当用户移动手指,这个闭包都会被调用
        }
        .onEnded { value in
            // 手势结束时调用,例如当用户松开手指
        }
)

1、DragGesture()

DragGesture 是一个手势,用于捕获用户在屏幕上的拖动行为。它提供了 onChanged 和 onEnded 两个主要的回调,允许你在手势过程中和手势结束时执行操作。

2、onChanged

onChanged 闭包在用户拖动手指时持续调用。它会不断接收手势的当前状态信息。通过 onChanged,你可以实时更新视图的位置、偏移量等属性。

例如,更新拖动的偏移量:

.onChanged { value in
    dragAmount = value.translation // 获取当前的拖动位移
}

3、onEnded

onEnded 闭包在手势结束时调用,例如当用户松开手指时。在这个闭包中,可以做一些收尾的动作,比如将视图恢复到初始位置或执行某些动画。

例如,拖动结束后,重置偏移量:

.onEnded { value in
    withAnimation {
        dragAmount = .zero // 动画回到初始位置
    }
}

在onChanged和onEnded中可以使用 $0:

.onChanged {
    dragOffset = $0.translation // 实时更新
}

$0 表示传递给闭包的第一个参数,它是 DragGesture.Value 类型的实例,包含了很多与当前手势相关的信息。

DragGesture.Value 中的所有可用属性

startLocation: 手势的起始位置(CGPoint)。

location: 当前手势的位置(CGPoint)。

translation: 当前手势的偏移量(CGSize)。

predictedEndLocation: 预测的手势结束位置(CGPoint),基于当前的速度。

predictedEndTranslation: 预测的手势结束时的位移(CGSize)。

time: 手势的时间长度(TimeInterval),记录手势持续了多长时间。

DragGesture.Value 的属性

在 onChanged 和 onEnded 闭包中,传递的 value 是 DragGesture.Value 类型的实例,包含以下常用属性:

1、startLocation:手势的起始位置,即手指第一次触碰屏幕的位置。类型为 CGPoint。

let startLocation: CGPoint = value.startLocation

2、location:当前手势的位置,即手指的当前位置。类型为 CGPoint。

let location: CGPoint = value.location

3、translation:从 startLocation 到 location 的位移(拖动的偏移量)。类型为 CGSize,包含 width 和 height 两个值,分别代表水平方向和垂直方向的偏移量。

let translation: CGSize = value.translation

4、time:手势从开始到当前时间的时长。类型为 TimeInterval,记录的是手势持续了多长时间。

let duration: TimeInterval = value.time

使用方法

基本用法

struct ContentView: View {
    @State private var offset = CGSize.zero // 偏移量

    var body: some View {
        Text("Drag me!")
            .padding()
            .background(Color.blue)
            .cornerRadius(10)
            .offset(offset) // 根据手势更新位置
            .gesture(
                DragGesture()
                    .onChanged { value in
                        // 实时更新偏移量
                        offset = value.translation
                    }
                    .onEnded { _ in
                        // 手势结束后复位
                        offset = .zero
                    }
            )
    }
}

效果

Text 会跟随手指拖动。

当手指抬起时,回到原来的位置。

拖动多视图

struct ContentView: View {
    @State private var circleOffset = CGSize.zero
    @State private var squareOffset = CGSize.zero

    var body: some View {
        VStack {
            Circle()
                .fill(Color.red)
                .frame(width: 100, height: 100)
                .offset(circleOffset)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            circleOffset = value.translation
                        }
                        .onEnded { _ in
                            circleOffset = .zero
                        }
                )

            Rectangle()
                .fill(Color.green)
                .frame(width: 100, height: 100)
                .offset(squareOffset)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            squareOffset = value.translation
                        }
                        .onEnded { _ in
                            squareOffset = .zero
                        }
                )
        }
    }
}

效果

圆形和矩形可以独立拖动。

配合动画

struct ContentView: View {
    @State private var offset = CGSize.zero

    var body: some View {
        Circle()
            .fill(Color.orange)
            .frame(width: 100, height: 100)
            .offset(offset)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        offset = value.translation
                    }
                    .onEnded { _ in
                        // 使用动画复位
                        withAnimation {
                            offset = .zero
                        }
                    }
            )
    }
}

效果

当手指抬起时,视图会带有动画返回到原来的位置。

拖动限制范围

struct ContentView: View {
    @State private var offset = CGSize.zero

    var body: some View {
        Circle()
            .fill(Color.purple)
            .frame(width: 100, height: 100)
            .offset(offset)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        // 限制拖动范围
                        offset = CGSize(
                            width: min(max(value.translation.width, -100), 100),
                            height: min(max(value.translation.height, -100), 100)
                        )
                    }
                    .onEnded { _ in
                        withAnimation {
                            offset = .zero
                        }
                    }
            )
    }
}

效果

拖动范围被限制在一个 200×200 的区域。

拖动手势的回调

DragGesture 提供了两个回调方法:

onChanged

在手势变化时触发,实时返回拖动的状态。

参数

value.translation:相对于手势开始的偏移量。

onEnded

当手势结束时触发。

参数

value.translation:手势结束时的总拖动偏移量。

示例:onChanged 和 onEnded

struct ContentView: View {
    @State private var position = CGSize.zero
    @State private var dragOffset = CGSize.zero

    var body: some View {
        Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .offset(x: position.width + dragOffset.width, y: position.height + dragOffset.height)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        dragOffset = value.translation // 实时更新
                    }
                    .onEnded { value in
                        position.width += value.translation.width
                        position.height += value.translation.height
                        dragOffset = .zero
                    }
            )
    }
}

效果

圆形会随着手指拖动,并在释放手指后保持最终位置。

拖动手势的典型应用

1、视图拖拽

在屏幕上移动视图的位置。

2、滑动删除

实现类似列表项的滑动操作。

3、拖动排序

用于重新排列项目的顺序。

4、拖拽和放置(Drag and Drop)

将拖拽与放置区域结合,实现更复杂的交互。

5、注意事项

1、与其他手势结合

可以使用 simultaneously(with:) 或 sequenced(before:) 将拖动手势与其他手势组合。

2、与 offset 一起使用

通常需要结合 offset 来更新视图的位置。

3、手势范围的坐标系

默认坐标系是 .local,可以切换为 .global 来检测屏幕级别的拖动。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注