在 SwiftUI 中处理双重和三重手势识别器
Handling both double and triple gesture recognisers in SwiftUI
我试图在 SwiftUI 中区分双击和三次点击手势识别器。在基于故事板的应用程序中,可以使用 UIGestureRecognizerDelegate
完成,但在 SwiftUI 中我还没有找到任何解决方案。
注意:
以下无效:
.onTapGesture(count: 3) {
print("Triple Tap!")
}
.onTapGesture(count: 2) {
print("Double Tap!")
}
import SwiftUI
struct MultipleTaps: View {
let doubleTap = TapGesture(count: 2).onEnded({print("Double Tap!")})
let tripleTap = TapGesture(count: 3).onEnded({print("Triple Tap!")})
var body: some View {
let tripleBeforedouble = tripleTap.exclusively(before: doubleTap)
return Text("Hello World!").gesture(tripleBeforedouble)
}
}
struct MultipleTaps_Previews: PreviewProvider {
static var previews: some View {
MultipleTaps()
}
}
为此,您可以使用 .gesture
和 .simultaneously(with:)
。来自文档:将一个手势与另一个手势结合起来,创建一个同时识别这两个手势的新手势。
示例代码是
struct TwoGesturesView: View {
var body: some View {
Text("Hello World!")
.gesture(TapGesture(count: 2).onEnded({
print("Double Tap")
})
.simultaneously(with: TapGesture(count: 3).onEnded({
print("Triple Tap")
})))
}
}
为了在项目中有一些适合每个人需要的多手势,Apple 除了正常手势之外没有什么可以提供的,将它们混合在一起以达到想要的手势有时会变得棘手,这是一个救赎,没有问题或错误!
一个名为tapCountRecognizer的自定义零问题手势,我们可以将它应用到任何View。用于同时使用单击、双击和三次点击手势。
注意你也可以这样使用,只是为了doubleTap和tripleTap :
.tapCountRecognizer(tapSensitivity: 250, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
或:
.tapCountRecognizer(doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
import SwiftUI
struct ContentView: View {
var body: some View {
Circle()
.fill(Color.red)
.frame(width: 100, height: 100, alignment: .center)
.tapCountRecognizer(tapSensitivity: 250, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
.shadow(radius: 10)
.statusBar(hidden: true)
}
func singleTapAction() { print("singleTapAction") }
func doubleTapAction() { print("doubleTapAction") }
func tripleTapAction() { print("tripleTapAction") }
}
struct TapCountRecognizerModifier: ViewModifier {
let tapSensitivity: Int
let singleTapAction: (() -> Void)?
let doubleTapAction: (() -> Void)?
let tripleTapAction: (() -> Void)?
init(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) {
self.tapSensitivity = ((tapSensitivity >= 0) ? tapSensitivity : 250)
self.singleTapAction = singleTapAction
self.doubleTapAction = doubleTapAction
self.tripleTapAction = tripleTapAction
}
@State private var tapCount: Int = Int()
@State private var currentDispatchTimeID: DispatchTime = DispatchTime.now()
func body(content: Content) -> some View {
return content
.gesture(fundamentalGesture)
}
var fundamentalGesture: some Gesture {
DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
.onEnded() { _ in tapCount += 1; tapAnalyzerFunction() }
}
func tapAnalyzerFunction() {
currentDispatchTimeID = dispatchTimeIdGenerator(deadline: tapSensitivity)
if tapCount == 1 {
let singleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: singleTapGestureDispatchTimeID) {
if (singleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedSingleTapAction: () -> Void = singleTapAction { unwrappedSingleTapAction() }
tapCount = 0
}
}
}
else if tapCount == 2 {
let doubleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: doubleTapGestureDispatchTimeID) {
if (doubleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedDoubleTapAction: () -> Void = doubleTapAction { unwrappedDoubleTapAction() }
tapCount = 0
}
}
}
else {
if let unwrappedTripleTapAction: () -> Void = tripleTapAction { unwrappedTripleTapAction() }
tapCount = 0
}
}
func dispatchTimeIdGenerator(deadline: Int) -> DispatchTime { return DispatchTime.now() + DispatchTimeInterval.milliseconds(deadline) }
}
extension View {
func tapCountRecognizer(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) -> some View {
return self.modifier(TapCountRecognizerModifier(tapSensitivity: tapSensitivity, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction))
}
}
这是正确的方法,如果我可以自己说的话!
在 iOS 14.4.2、Xcode 12.5 beta 3 上测试。
使用此 tapGesture,仅点击一次后没有任何反应,双击后您将在控制台中看到 "double tap"
,点击三次或更多次后您将在控制台中看到 "triple tap"
.
struct ContentView: View {
var tapGesture: some Gesture {
SimultaneousGesture(TapGesture(count: 2), TapGesture(count: 3))
.onEnded { gestureValue in
if gestureValue.second != nil {
print("triple tap!")
} else if gestureValue.first != nil {
print("double tap!")
}
}
}
var body: some View {
Text("Hello World!")
.gesture(tapGesture)
}
}
我试图在 SwiftUI 中区分双击和三次点击手势识别器。在基于故事板的应用程序中,可以使用 UIGestureRecognizerDelegate
完成,但在 SwiftUI 中我还没有找到任何解决方案。
注意:
以下无效:
.onTapGesture(count: 3) {
print("Triple Tap!")
}
.onTapGesture(count: 2) {
print("Double Tap!")
}
import SwiftUI
struct MultipleTaps: View {
let doubleTap = TapGesture(count: 2).onEnded({print("Double Tap!")})
let tripleTap = TapGesture(count: 3).onEnded({print("Triple Tap!")})
var body: some View {
let tripleBeforedouble = tripleTap.exclusively(before: doubleTap)
return Text("Hello World!").gesture(tripleBeforedouble)
}
}
struct MultipleTaps_Previews: PreviewProvider {
static var previews: some View {
MultipleTaps()
}
}
为此,您可以使用 .gesture
和 .simultaneously(with:)
。来自文档:将一个手势与另一个手势结合起来,创建一个同时识别这两个手势的新手势。
示例代码是
struct TwoGesturesView: View {
var body: some View {
Text("Hello World!")
.gesture(TapGesture(count: 2).onEnded({
print("Double Tap")
})
.simultaneously(with: TapGesture(count: 3).onEnded({
print("Triple Tap")
})))
}
}
为了在项目中有一些适合每个人需要的多手势,Apple 除了正常手势之外没有什么可以提供的,将它们混合在一起以达到想要的手势有时会变得棘手,这是一个救赎,没有问题或错误!
一个名为tapCountRecognizer的自定义零问题手势,我们可以将它应用到任何View。用于同时使用单击、双击和三次点击手势。
注意你也可以这样使用,只是为了doubleTap和tripleTap :
.tapCountRecognizer(tapSensitivity: 250, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
或:
.tapCountRecognizer(doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
import SwiftUI
struct ContentView: View {
var body: some View {
Circle()
.fill(Color.red)
.frame(width: 100, height: 100, alignment: .center)
.tapCountRecognizer(tapSensitivity: 250, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction)
.shadow(radius: 10)
.statusBar(hidden: true)
}
func singleTapAction() { print("singleTapAction") }
func doubleTapAction() { print("doubleTapAction") }
func tripleTapAction() { print("tripleTapAction") }
}
struct TapCountRecognizerModifier: ViewModifier {
let tapSensitivity: Int
let singleTapAction: (() -> Void)?
let doubleTapAction: (() -> Void)?
let tripleTapAction: (() -> Void)?
init(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) {
self.tapSensitivity = ((tapSensitivity >= 0) ? tapSensitivity : 250)
self.singleTapAction = singleTapAction
self.doubleTapAction = doubleTapAction
self.tripleTapAction = tripleTapAction
}
@State private var tapCount: Int = Int()
@State private var currentDispatchTimeID: DispatchTime = DispatchTime.now()
func body(content: Content) -> some View {
return content
.gesture(fundamentalGesture)
}
var fundamentalGesture: some Gesture {
DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
.onEnded() { _ in tapCount += 1; tapAnalyzerFunction() }
}
func tapAnalyzerFunction() {
currentDispatchTimeID = dispatchTimeIdGenerator(deadline: tapSensitivity)
if tapCount == 1 {
let singleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: singleTapGestureDispatchTimeID) {
if (singleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedSingleTapAction: () -> Void = singleTapAction { unwrappedSingleTapAction() }
tapCount = 0
}
}
}
else if tapCount == 2 {
let doubleTapGestureDispatchTimeID: DispatchTime = currentDispatchTimeID
DispatchQueue.main.asyncAfter(deadline: doubleTapGestureDispatchTimeID) {
if (doubleTapGestureDispatchTimeID == currentDispatchTimeID) {
if let unwrappedDoubleTapAction: () -> Void = doubleTapAction { unwrappedDoubleTapAction() }
tapCount = 0
}
}
}
else {
if let unwrappedTripleTapAction: () -> Void = tripleTapAction { unwrappedTripleTapAction() }
tapCount = 0
}
}
func dispatchTimeIdGenerator(deadline: Int) -> DispatchTime { return DispatchTime.now() + DispatchTimeInterval.milliseconds(deadline) }
}
extension View {
func tapCountRecognizer(tapSensitivity: Int = 250, singleTapAction: (() -> Void)? = nil, doubleTapAction: (() -> Void)? = nil, tripleTapAction: (() -> Void)? = nil) -> some View {
return self.modifier(TapCountRecognizerModifier(tapSensitivity: tapSensitivity, singleTapAction: singleTapAction, doubleTapAction: doubleTapAction, tripleTapAction: tripleTapAction))
}
}
这是正确的方法,如果我可以自己说的话!
在 iOS 14.4.2、Xcode 12.5 beta 3 上测试。
使用此 tapGesture,仅点击一次后没有任何反应,双击后您将在控制台中看到 "double tap"
,点击三次或更多次后您将在控制台中看到 "triple tap"
.
struct ContentView: View {
var tapGesture: some Gesture {
SimultaneousGesture(TapGesture(count: 2), TapGesture(count: 3))
.onEnded { gestureValue in
if gestureValue.second != nil {
print("triple tap!")
} else if gestureValue.first != nil {
print("double tap!")
}
}
}
var body: some View {
Text("Hello World!")
.gesture(tapGesture)
}
}