我需要在构建的应用程序中执行此操作,所以想要分享一下。请参考下面的代码,它相对而言比较易于理解,并且在SwiftUI的主要元素内保持一致。
这个答案与上面的答案之间的主要区别在于,它允许根据状态更新按钮的背景颜色,并且还涵盖了希望长按操作在手指抬起时而不是在时间阈值过去时发生的用例。
如其他人所述,我无法将手势直接应用于Button,因此必须将其应用于其中的TextView。这具有不幸的副作用,即减少了按钮的可点击范围。如果我在按钮的边缘附近按下,则手势不会触发。因此,我删除了Button并专注于直接操作我的TextView对象(这可以替换为ImageView或其他视图(但不能是Button!))。
下面的代码设置了三个手势:
一个LongPressGesture立即触发,并反映您问题中的“tap”手势(我没有测试,但可以替换为TapGesture)
另一个LongPressGesture的最小持续时间为0.25,并反映了您问题中的“长按”手势
带有最小距离为0的拖动手势,允许我们在手指离开按钮时执行事件,并且不会自动在0.25秒后执行(如果这不是您的用例,则可以删除此操作)。您可以在此处阅读更多: 如何检测SwiftUI触摸事件而没有移动或持续时间?
我们按以下顺序对手势进行排序:使用“独占”将“长按”(即上述2和3组合)与Tap(上面的第一个手势)结合起来,如果未达到“长按”的0.25秒阈值,则执行tap手势。“长按”本身是我们的长按手势和拖动手势的序列,因此只有当我们抬起手指时才执行该操作。
我还添加了代码,以根据状态更新按钮的颜色。需要注意的一点是,我必须在长按和拖动手势的onEnded部分中添加按钮颜色的代码,因为极小的处理时间不幸地导致按钮在长按手势和拖动手势之间切换回darkButton颜色(理论上不应该发生这种情况,除非我某些地方有漏洞!)。
您可以在此处阅读有关手势的更多信息: https://developer.apple.com/documentation/swiftui/gestures/composing_swiftui_gestures
如果您修改下面的代码并注意苹果有关手势的注释(这篇答案也很有用:
如何在SwiftUI中触发事件处理程序,当用户停止长按手势时?),您应该能够设置复杂的自定义按钮交互。使用手势作为构建块,并将它们组合在一起以消除单个手势内的任何缺陷(例如,longPressGesture没有选项在其结束时执行事件而不是在达到条件时执行)。
附言:我有一个全局环境对象'dataRouter'(与问题无关,只是我选择在Swift视图间共享参数的方式),您可以安全地将其编辑掉。
struct AdvanceButton: View {
@EnvironmentObject var dataRouter: DataRouter
@State var width: CGFloat
@State var height: CGFloat
@State var bgColor: Color
@GestureState var longPress = false
@GestureState var longDrag = false
var body: some View {
let longPressGestureDelay = DragGesture(minimumDistance: 0)
.updating($longDrag) { currentstate, gestureState, transaction in
gestureState = true
}
.onEnded { value in
print(value.translation)
print("long press action goes here")
self.bgColor = self.dataRouter.darkButton
}
let shortPressGesture = LongPressGesture(minimumDuration: 0)
.onEnded { _ in
print("short press goes here")
}
let longTapGesture = LongPressGesture(minimumDuration: 0.25)
.updating($longPress) { currentstate, gestureState, transaction in
gestureState = true
}
.onEnded { _ in
self.bgColor = self.dataRouter.lightButton
}
let tapBeforeLongGestures = longTapGesture.sequenced(before:longPressGestureDelay).exclusively(before: shortPressGesture)
return
Text("9")
.font(self.dataRouter.fontStyle)
.foregroundColor(self.dataRouter.darkButtonText)
.frame(width: width, height: height)
.background(self.longPress ? self.dataRouter.lightButton : (self.longDrag ? self.dataRouter.brightButton : self.bgColor))
.cornerRadius(15)
.gesture(tapBeforeLongGestures)
}
}