TL;DR: 在 SwiftUI 中实现多指点击手势。在 iOS 18 及以上版本使用
UIGestureRecognizerRepresentable
桥接 UIKit 手势,在低版本中通过UIViewRepresentable
封装实现。
背景
SwiftUI 的 TapGesture
手势仅支持设置连击次数,不支持多指点击。本指南将介绍在不同 iOS 版本中实现多指点击的方法。
iOS 18+
从 iOS 18 开始,可以使用 UIGestureRecognizerRepresentable
将 UIKit 手势直接桥接到 SwiftUI,实现多指点击功能。
示例代码
Swift
struct TowFingerTapDemo: View {
var body: some View {
Rectangle()
.foregroundStyle(.orange)
.frame(width: 200, height: 200)
.onTapGesture {
print("One Touch")
}
// 应用 TwoFingerTapGesture
.gesture(TwoFingerTapGesture {
print("Two Touches")
})
}
}
struct TwoFingerTapGesture: UIGestureRecognizerRepresentable {
let action: () -> Void
func makeUIGestureRecognizer(context: Context) -> some UIGestureRecognizer {
let gesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleGesture))
gesture.numberOfTouchesRequired = 2 // 设置双指点击
gesture.delegate = context.coordinator
return gesture
}
func makeCoordinator(converter: CoordinateSpaceConverter) -> Coordinator {
Coordinator(action: action)
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
let action: () -> Void
init(action: @escaping () -> Void) {
self.action = action
}
@objc func handleGesture() {
action()
}
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool {
true // 支持多手势并行
}
}
}
特点
UIGestureRecognizerRepresentable
类似于UIViewRepresentable
,可桥接 UIKit 手势。- 可通过
Coordinator
自定义更详尽的手势处理逻辑。
在低版本中
在低于 iOS 18 的版本中,需通过 UIViewRepresentable
自定义视图,将 UIKit 手势封装为 SwiftUI 兼容组件。
实现步骤
- 创建支持双指点击的
UIView
。 - 通过
UIViewRepresentable
封装为 SwiftUI 视图。 - 扩展 SwiftUI
View
,支持双指点击功能。
示例代码
Swift
struct TowFingerTapDemo: View {
var body: some View {
Rectangle()
.foregroundStyle(.orange)
.frame(width: 200, height: 200)
.onTwoFingerTap {
print("Two Touches")
}
.onTapGesture {
print("One Touch")
}
}
}
extension View {
func onTwoFingerTap(perform action: @escaping () -> Void) -> some View {
overlay(TwoFingerTapLayer(action: action))
}
}
struct TwoFingerTapLayer: UIViewRepresentable {
let action: () -> Void
func makeUIView(context: Context) -> some UIView {
let view = TwoFingerTapUIView(action: action)
view.backgroundColor = .clear
return view
}
func updateUIView(_ uiView: Self.UIViewType, context: Self.Context) {}
}
class TwoFingerTapUIView: UIView {
let action: () -> Void
private var gesture: UITapGestureRecognizer!
init(action: @escaping () -> Void) {
self.action = action
super.init(frame: .zero)
setupGesture()
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupGesture() {
gesture = UITapGestureRecognizer(target: self, action: #selector(handleGesture))
gesture.numberOfTouchesRequired = 2 // 设置双指点击
addGestureRecognizer(gesture)
}
@objc private func handleGesture() {
action()
}
}
注意事项
- 手势顺序:确保
onTapGesture
等原生手势位于自定义 UIKit 手势之后,以避免冲突。
Swift
// 正确顺序
.onTwoFingerTap { print("Two Touches") }
.onTapGesture { print("One Touch") }