手势基础
在 SwiftUI 中,手势是通过各种手势类型和修饰符来实现的,它们可以附加到任何视图上,以响应用户的交互。
常用手势类型
1. 点击手势
点击手势通过 onTapGesture 修饰符实现,用于检测用户的点击操作。
示例代码:
swift
@State private var isTapped = false
Rectangle()
.fill(.blue)
.frame(width: 200, height: 100)
.cornerRadius(10)
.onTapGesture {
isTapped.toggle()
}
点击次数:
可以通过 count 参数指定点击次数,例如双击:
swift
Rectangle()
.onTapGesture(count: 2) {
tapCount += 1
}
2. 长按手势
长按手势通过 onLongPressGesture 修饰符实现,用于检测用户的长按操作。
示例代码:
swift
@State private var isLongPressed = false
Rectangle()
.fill(.green)
.frame(width: 200, height: 100)
.cornerRadius(10)
.onLongPressGesture {
isLongPressed.toggle()
}
3. 拖拽手势
拖拽手势通过 DragGesture 实现,用于检测用户的拖拽操作。
示例代码:
swift
@State private var dragOffset = CGSize.zero
Circle()
.fill(.red)
.frame(width: 50, height: 50)
.offset(dragOffset)
.gesture(
DragGesture()
.onChanged { value in
dragOffset = value.translation
}
.onEnded { value in
// 可以在这里添加结束拖动的逻辑
}
)
4. 缩放手势
缩放手势通过 MagnificationGesture 实现,用于检测用户的缩放操作。
示例代码:
swift
@State private var scale = 1.0
Rectangle()
.fill(.purple)
.frame(width: 200, height: 200)
.cornerRadius(10)
.scaleEffect(scale)
.gesture(
MagnificationGesture()
.onChanged { value in
scale = value
}
)
5. 旋转手势
旋转手势通过 RotationGesture 实现,用于检测用户的旋转操作。
示例代码:
swift
@State private var rotation = 0.0
Rectangle()
.fill(.orange)
.frame(width: 200, height: 200)
.cornerRadius(10)
.rotationEffect(.degrees(rotation))
.gesture(
RotationGesture()
.onChanged { value in
rotation = value.degrees
}
)
组合手势
组合手势是将多种手势效果结合在一起,可以使用 .simultaneousGesture() 修饰符。
示例代码:
swift
@State private var offset = CGSize.zero
@State private var scale = 1.0
@State private var rotation = 0.0
Rectangle()
.fill(.blue)
.frame(width: 200, height: 200)
.cornerRadius(10)
.offset(offset)
.scaleEffect(scale)
.rotationEffect(.degrees(rotation))
.gesture(
DragGesture()
.onChanged { value in
offset = value.translation
}
)
.simultaneousGesture(
MagnificationGesture()
.onChanged { value in
scale = value
}
)
.simultaneousGesture(
RotationGesture()
.onChanged { value in
rotation = value.degrees
}
)
手势状态管理
手势通常与状态管理结合使用,以追踪手势的状态和数据。
示例代码:
swift
@State private var isDragging = false
Rectangle()
.fill(.purple)
.frame(width: 100, height: 100)
.cornerRadius(10)
.gesture(
DragGesture()
.onChanged { _ in
isDragging = true
}
.onEnded { _ in
isDragging = false
}
)
实践示例:完整手势演示
以下是一个完整的手势演示示例,包含了各种手势类型和组合:
swift
import SwiftUI
struct GestureAndInteractionDemo: View {
// 状态管理
@State private var isTapped = false
@State private var isLongPressed = false
@State private var offset = CGSize.zero
@State private var scale = 1.0
@State private var rotation = 0.0
@State private var dragOffset = CGSize.zero
@State private var isDragging = false
@State private var tapCount = 0
var body: some View {
ScrollView {
VStack(spacing: 25) {
// 标题
Text("手势与交互")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.blue)
// 1. 点击手势
VStack {
Text("1. 点击手势")
.font(.headline)
Rectangle()
.fill(isTapped ? Color.green : Color.blue)
.frame(width: 200, height: 80)
.cornerRadius(10)
.onTapGesture {
withAnimation {
isTapped.toggle()
}
}
Text("状态: \(isTapped ? "已点击" : "未点击")")
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 2. 长按手势
VStack {
Text("2. 长按手势")
.font(.headline)
Rectangle()
.fill(isLongPressed ? Color.red : Color.green)
.frame(width: 200, height: 80)
.cornerRadius(10)
.onLongPressGesture {
withAnimation {
isLongPressed.toggle()
}
}
Text("状态: \(isLongPressed ? "长按中" : "未长按")")
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 3. 拖拽手势
VStack {
Text("3. 拖拽手势")
.font(.headline)
Circle()
.fill(.red)
.frame(width: 60, height: 60)
.offset(dragOffset)
.gesture(
DragGesture()
.onChanged { value in
dragOffset = value.translation
}
.onEnded { _ in
withAnimation {
dragOffset = .zero
}
}
)
Text("拖拽小球后自动归位")
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 4. 缩放手势
VStack {
Text("4. 缩放手势")
.font(.headline)
Rectangle()
.fill(.purple)
.frame(width: 120, height: 120)
.cornerRadius(10)
.scaleEffect(scale)
.gesture(
MagnificationGesture()
.onChanged { value in
scale = value
}
.onEnded { _ in
withAnimation {
scale = 1.0
}
}
)
Text("双指缩放,松手复位")
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 5. 旋转手势
VStack {
Text("5. 旋转手势")
.font(.headline)
Rectangle()
.fill(.orange)
.frame(width: 120, height: 120)
.cornerRadius(10)
.rotationEffect(.degrees(rotation))
.gesture(
RotationGesture()
.onChanged { value in
rotation = value.degrees
}
.onEnded { _ in
withAnimation {
rotation = 0
}
}
)
Text("双指旋转,松手复位")
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 6. 组合手势
VStack {
Text("6. 组合手势")
.font(.headline)
Rectangle()
.fill(.blue)
.frame(width: 120, height: 120)
.cornerRadius(10)
.offset(offset)
.scaleEffect(scale)
.rotationEffect(.degrees(rotation))
.gesture(
DragGesture()
.onChanged { value in
offset = value.translation
}
.onEnded { _ in
withAnimation {
offset = .zero
}
}
)
.simultaneousGesture(
MagnificationGesture()
.onChanged { value in
scale = value
}
.onEnded { _ in
withAnimation {
scale = 1.0
}
}
)
.simultaneousGesture(
RotationGesture()
.onChanged { value in
rotation = value.degrees
}
.onEnded { _ in
withAnimation {
rotation = 0
}
}
)
Text("支持拖动、缩放、旋转")
.font(.caption)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
// 7. 双击计数
VStack {
Text("7. 双击计数")
.font(.headline)
Rectangle()
.fill(.teal)
.frame(width: 200, height: 80)
.cornerRadius(10)
.onTapGesture(count: 2) {
tapCount += 1
}
Text("双击次数: \(tapCount)")
Button("重置") {
tapCount = 0
}
.buttonStyle(.bordered)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
}
.padding()
}
}
}
#Preview {
GestureAndInteractionDemo()
}
常见问题与解决方案
1. 手势不响应
问题:视图添加了手势,但没有响应。
解决方案:
- 确保视图有足够的大小(例如,不要将手势添加到
frame(width: 0, height: 0)的视图上) - 检查视图是否被其他视图遮挡(使用
.contentShape(Rectangle())扩大可点击区域) - 确保没有其他手势冲突
swift
// 扩大点击区域
Rectangle()
.fill(.clear)
.contentShape(Rectangle()) // 使透明区域也能响应手势
.onTapGesture { }
2. 组合手势冲突
问题:多个手势同时应用时出现冲突。
解决方案:
- 使用
.simultaneousGesture()修饰符来允许同时处理多个手势 - 使用
.highPriorityGesture()让某个手势优先 - 使用
.gesture()的including参数控制手势识别行为
swift
// 高优先级手势(会阻断其他手势)
view.highPriorityGesture(
TapGesture().onEnded { }
)
// 同时识别多个手势
view.simultaneousGesture(dragGesture)
.simultaneousGesture(rotationGesture)
3. 手势状态管理
问题:手势结束后状态没有正确更新。
解决方案:
- 在
.onEnded回调中正确更新状态 - 对于需要持久化的状态,使用
@State或@StateObject
swift
DragGesture()
.onChanged { value in
// 实时更新
offset = value.translation
}
.onEnded { value in
// 结束时的处理
withAnimation {
offset = .zero
}
}
总结
本章介绍了 SwiftUI 中的手势与交互,包括:
- 基本手势类型:点击、长按、拖拽、缩放、旋转
- 手势的状态管理和数据处理
- 组合手势 的实现方法(
.simultaneousGesture) - 手势的高级应用技巧(优先级控制、自定义识别)
通过这些手势,可以使应用界面更加交互友好,提升用户体验。在实际开发中,合理使用手势可以为应用增添交互性,使界面操作更加直观自然。
参考资料
- SwiftUI 官方文档 - Gestures
- Apple Developer Documentation: TapGesture
- Apple Developer Documentation: DragGesture
- Apple Developer Documentation: MagnificationGesture
- Apple Developer Documentation: RotationGesture
- WWDC 2019: Building Custom Views with SwiftUI
本内容为《SwiftUI 进阶》第三章,欢迎关注后续更新。