SwiftUI - 手势
手势用于处理用户与应用中的 UI 组件之间的交互。或者我们可以说手势处理触摸事件。它通过响应用户的触摸和动作,为所有 Apple 平台创建交互式和响应式应用。
SwiftUI 支持各种内置手势,例如点击、拖动、滑动等,我们还可以创建自定义手势来满足应用的要求。
在这里,手势通常支持单指交互,SwiftUI 不提供任何特殊功能来设置移动的总手指数。手势取代了传统按钮,与按钮相比提供了更多功能。
SwiftUI 中的手势类型
SwiftUI 为不同类型的交互提供了各种手势。它既支持预定义手势,也支持自定义手势。一些常用的手势如下−
点击手势
点击手势用于识别视图上的单个或多个点击。它是最常用和最简单的手势。我们可以借助 .onTapGesture() 修饰符来实现点击手势。此修饰符只要找到点击手势,就会执行指定的操作。
语法
以下是语法 −
func onTapGesture(count:Int, perfrom: action)
参数
以下是修饰符 −
接受的参数count:表示点击总数。默认情况下,该值为 1。
action:表示要执行的操作。
示例
以下 SwiftUI 程序执行点击手势。在这里,当我们点击形状时,形状从胶囊变为矩形。
import SwiftUI
struct ContentView: View {
@State private var isShape = true
var body: some View {
Group {
if isShape {
Capsule()
.fill(.green)
.frame(width: 100, height: 50)
.overlay(Text("Tap Me").font(.title2))
} else {
Rectangle().fill(.blue).frame(width: 100, height: 100)
}
}.onTapGesture {
// 在胶囊和矩形之间切换形状
isShape.toggle()
}
}
}
#Preview {
ContentView()
}
输出
长按手势
长按手势用于识别视图上的长按。此手势通过使用 .onLongPressGesture() 修饰符激活。此修饰符插入一个操作,该操作将在用户长按给定 UI 组件或视图时执行。
语法
以下是语法−
func onLongPressGesture(minimumDuration:Double, perfrom:action, onPressingChanged: Bool)
参数
以下是修饰符−接受的参数
minimumDuration:表示长按的最小持续时间。
action:表示要执行的操作。
onPressingChanged:表示当手势的按下状态改变时将运行的闭包。
示例
以下 SwiftUI 程序执行长按手势。
import SwiftUI
struct ContentView: View {
@State private var isPress = false
var body: some View {
Rectangle()
.fill(isPress ? .red : .yellow)
.frame(width: 100, height: 100)
.gesture(
// 按住 0.5 秒
LongPressGesture(minimumDuration: 0.5).onEnded { _ in
// 长按结束时切换颜色
isPress.toggle()
}
)
}
}
#Preview {
ContentView()
}
输出
DragGesture
拖动手势用于跟踪用户手指在屏幕上的移动。它允许用户通过在屏幕上拖动视图来更新视图的位置。我们可以借助 DragGesture() 方法实现拖动手势。此方法在手势成功之前创建一个具有最小拖动距离的拖动手势,并且还包含手势位置的坐标。此方法始终在 .gesture() 修饰符内使用。
语法
以下是语法 −
func DragGesture(minimumDistance:CGFloat, coordinateSpace:coordinates)
参数
以下是修饰符 − 接受的参数
minimumDistance:表示最小拖动距离。
coordinateSpace:表示执行拖动手势的坐标空间。
示例
以下 SwiftUI 程序执行拖动手势。
import SwiftUI
struct ContentView: View {
// 追踪位置
@State private var position = CGSize.zero
// 跟踪拖动偏移
@State private var dragOffsetValue = CGSize.zero
var body: some View {
VStack {
Text("Drag the Text").font(.title)
// 可拖拽文本
Text("TutorialsPoint")
.font(.largeTitle)
.frame(width: 200)
.foregroundStyle(.red)
.offset(x: position.width + dragOffsetValue.width, y: position.height + dragOffsetValue.height)
.gesture(
DragGesture().onChanged { g in
// 拖动过程中更新偏移值
dragOffsetValue = g.translation
}.onEnded { _ in
// 拖动完成后更新位置。
position.width += dragOffsetValue.width
position.height += dragOffsetValue.height
dragOffsetValue = .zero
}
).animation(.easeIn, value: position)
}
}
}
#Preview {
ContentView()
}
输出
MagnifyGesture
放大手势用于跟踪放大动作或捏合缩放手势。此手势通过使用 MagnifyGesture() 方法实现。此方法创建一个放大手势,我们可以通过该手势缩放视图。它也用于 .gesture() 修饰符内。
语法
以下是语法−
func MagnifyGesture(minimumDelta:CGFloat)
参数
它仅需要一个参数,即启动手势的最小缩放值。
示例
以下 SwiftUI 程序执行放大手势。
import SwiftUI
struct ContentView: View {
// 跟踪当前比例
@State private var initialScale: CGFloat = 1.0
// 手势开始前轨道缩放
@State private var finalScale: CGFloat = 1.0
var body: some View {
Rectangle()
.fill(.red)
.frame(width: 200, height: 200)
// 应用缩放因子
.scaleEffect(initialScale)
.gesture(
MagnificationGesture().onChanged { x in
// 根据放大倍数更新比例
initialScale = finalScale * x
}.onEnded { _ in
// 手势结束时保存比例
finalScale = initialScale
}
).animation(.easeInOut, value: initialScale)
}
}
#Preview {
ContentView()
}
输出
RotateGesture
旋转手势用于跟踪旋转运动,并允许我们将视图围绕其锚点旋转到指定角度。我们可以使用 RotateGesture() 方法创建旋转手势。此方法创建一个旋转手势,跟踪旋转运动,并且视图中的角度被旋转。它也用于 .gesture() 修饰符内部。
语法
以下是语法−
func RotateGesture(minimumAngleDelta: Angle)
参数
它只接受一个参数,即启动手势的最小角度。此参数的默认值为 0 度。
示例
以下 SwiftUI 程序执行旋转手势。
import SwiftUI
struct ContentView: View {
// 当前旋转角度
@State private var initialAngle: Angle = .zero
// 最后确认的角度
@State private var endAngle: Angle = .zero
var body: some View {
Text("Rotate Me!").font(.largeTitle).padding()
// 将旋转效果应用于文本
.rotationEffect(initialAngle)
.gesture(
RotationGesture()
.onChanged { x in
// 根据旋转手势更新角度
initialAngle = endAngle + x
}.onEnded { _ in
// 手势结束后的最终角度
endAngle = initialAngle
}
).animation(.easeInOut, value: initialAngle)
}
}
#Preview {
ContentView()
}
输出
在 SwiftUI 中组合手势
在 SwiftUI 中,我们可以组合多个手势,以便同时使用多个手势。或者通过组合多个手势,我们可以创建一个新手势。因此,要组合手势,我们可以遵循以下任何一种方法 −
同时
同时手势是一个容器,我们可以在其中使用两个同时运行的手势,而没有任何优先权。例如,我们同时结合旋转和点击手势,因此每当我们点击图像时,图像就会开始旋转。
语法
以下是语法−
func SimultaneousGesture(first: FirstGesture, second: SecondGesture)
参数
它需要两个参数来保存相同类型或不同类型的两个手势。
示例
以下 SwiftUI 程序在 SimultaneousGesture() 的帮助下同时运行两个点击和拖动手势。
import SwiftUI
struct ContentView: View {
// 跟踪点击状态
@State private var isTap = false
// 追踪当前位置
@State private var currentPosition = CGSize.zero
// 追踪拖动位置
@GestureState private var dragPosition = CGSize.zero
var body: some View {
Text("Tap Me and Drag Me")
.font(.largeTitle)
.background(isTap ? .brown : .green)
.foregroundStyle(.white)
.offset(x: currentPosition.width + dragPosition.width, y: currentPosition.height + dragPosition.height)
.gesture(
// 使用 SimultaneousGesture 运行两个手势
SimultaneousGesture(
TapGesture().onEnded {
// 点击时切换颜色
isTap.toggle()
},
DragGesture()
.updating($dragPosition) { x, dragPosition, _ in
// 更新拖拽偏移
dragPosition = x.translation
}.onEnded { x in
// 拖动完成后更新位置
currentPosition.width += x.translation.width
currentPosition.height += x.translation.height
}
)
).animation(.easeInOut, value: isTap)
}
}
#Preview {
ContentView()
}
输出
序列
序列用于按顺序应用两个手势,这里第二个手势是在完成第一个手势后识别的。或者我们可以说手势是按顺序识别的。当我们想在指定的手势序列之后触发动作时,它很有用。这里手势的优先级很重要。
语法
以下是语法−
func SequenceGesture(first: FirstGesture, second: SecondGesture)
参数
它需要两个参数来保存相同类型或不同类型的两个手势。
示例
以下 SwiftUI 程序在 SequenceGesture() 函数的帮助下按顺序运行两个手势(点击和拖动手势)。
import SwiftUI
struct ContentView: View {
//跟踪点击状态
@State private var isTap = false
// 追踪当前位置
@State private var currentPosition = CGSize.zero
// 追踪拖动位置
@GestureState private var dragPosition = CGSize.zero
var body: some View {
Text("Tap Me and Drag Me")
.font(.largeTitle)
.background(isTap ? .brown : .green)
.foregroundStyle(.white)
.offset(x: currentPosition.width + dragPosition.width, y: currentPosition.height + dragPosition.height)
.gesture(
// 使用 SequenceGesture 按顺序运行两个手势
SequenceGesture(
TapGesture().onEnded {
// 点击时切换颜色
isTap.toggle()
},
DragGesture()
.updating($dragPosition) { x, dragPosition, _ in
// 更新拖拽偏移
dragPosition = x.translation
}.onEnded { x in
// 拖动完成后更新位置
currentPosition.width += x.translation.width
currentPosition.height += x.translation.height
}
)
).animation(.easeInOut, value: isTap)
}
}
#Preview {
ContentView()
}
输出
独占
独占手势是一种由两个手势组成的手势,其中只有一个手势会被执行。默认情况下,系统将优先执行第一个手势。
语法
以下是语法−
func ExclusiveGesture(first: FirstGesture, second: SecondGesture)
参数
它需要两个参数来保存这两个手势。
示例
以下 SwiftUI 程序执行点击手势。
import SwiftUI
struct ContentView: View {
// 跟踪点击状态
@State private var isTap = false
// 追踪当前位置
@State private var currentPosition = CGSize.zero
// 追踪拖动位置
@GestureState private var dragPosition = CGSize.zero
var body: some View {
Text("Tap Me & Drag Me")
.font(.largeTitle)
.background(isTap ? .yellow : .pink)
.foregroundStyle(.white)
.offset(x: currentPosition.width + dragPosition.width, y: currentPosition.height + dragPosition.height)
.gesture(
// 使用 ExclusiveGesture() 按顺序运行两个手势
ExclusiveGesture(
TapGesture().onEnded {
// 点击时切换颜色
isTap.toggle()
},
DragGesture()
.updating($dragPosition) {
x, dragPosition, _ in
// 更新拖拽偏移
dragPosition = x.translation
}.onEnded { x in
// 拖动完成后更新位置
currentPosition.width += x.translation.width
currentPosition.height += x.translation.height
}
)
).animation(.easeInOut, value: isTap)
}
}
#Preview {
ContentView()
}
输出

