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() }
输出
