我想在开始按下按钮时执行一个操作,然后在停止按下按钮时执行一个操作。我一直在寻找一个简单的解决方案,但遇到了更复杂的配置。我从 BlueSpud 获得了一个非常简单且接近的选项。按钮操作未使用,所以我尝试:
struct MyView: View {
@State private var pressing = false
var body: some View {
Text("Button")
.background(self.pressing ? Color.red : Color.blue)
.gesture(DragGesture(minimumDistance: 0.0)
.onChanged { _ in
self.pressing = true
print("Pressing started and/or ongoing")
}
.onEnded { _ in
self.pressing = false
print("Pressing ended")
})
}
}
此代码的问题在于,如果您在按下时将手指拖出按钮区域,则 .onEnded 永远不会被调用,并且如果没有可靠的事件结束,该解决方案将不起作用。
我还尝试了 Apple 的示例来编写 SwiftUI 手势。它提供了对按下和未按下状态的非常一致的控制,但我似乎不知道在哪里插入我的动作:
struct PressedButton: View {
var startAction: ()->Void
var endAction: ()->Void
enum DragState {
case inactive
case pressing
case dragging(translation: CGSize)
var translation: CGSize {
switch self {
case .inactive, .pressing:
return .zero
case .dragging(let translation):
return translation
}
}
var isActive: Bool {
switch self {
case .inactive:
print("DragState inactive but I can't add my action here")
//self.endAction()
return false
case .pressing, .dragging:
return true
}
}
var isDragging: Bool {
switch self {
case .inactive, .pressing:
return false
case .dragging:
return true
}
}
}
@GestureState var dragState = DragState.inactive
var body: some View {
let longPressDrag = LongPressGesture(minimumDuration: 0.1)
.sequenced(before: DragGesture())
.updating($dragState) { value, state, transaction in
switch value {
// Long press begins.
case .first(true):
print("Long press begins. I can add my action here")
self.startAction()
state = .pressing
// Long press confirmed, dragging may begin.
case .second(true, let drag):
//print("Long press dragging")
state = .dragging(translation: drag?.translation ?? .zero)
// Dragging ended or the long press cancelled.
default:
print("Long press inactive but it doesn't get called")
state = .inactive
}
}
.onEnded { _ in
print("Long press ended but it doesn't get called")
}
return Text("Button")
.background(dragState.isActive ? Color.purple : Color.orange)
.gesture(longPressDrag)
}
}
不需要 UIView 和 Representable,
您可以使用内置的 SwiftUI isPressed 属性进行 Button 的配置,如下所示:
import SwiftUI
struct MyButtonStyle: ButtonStyle
{
func makeBody(configuration: Configuration) -> some View
{
if(configuration.isPressed)
{
// call your action here but don't change @State of current view
print("Button is pressed")
}
else
{
// call your stop-action here but don't change @State of current view
print("Button released")
}
return configuration.label
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(20)
.scaleEffect(configuration.isPressed ? 0.8 : 1)
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
}
}
struct ButtonTestView: View
{
var body: some View
{
Button("Press me")
{
print("Button action")
}
.buttonStyle(MyButtonStyle())
}
}
@main
struct TestApp: App
{
var body: some Scene
{
WindowGroup
{
ButtonTestView()
}
}
}
一旦原生 SwiftUI 不允许你现在想要实现的目标,我会推荐以下方法,它是有效的、可管理的,因此也是可靠的。
该演示展示了基于使用
UIGestureRecongnizer
/UIViewRepresentable
的简化代码,可以轻松扩展(例如,如果您想拦截touchesCanceled
、点击计数等)
import SwiftUI
import UIKit
class MyTapGesture : UITapGestureRecognizer {
var didBeginTouch: (()->Void)?
var didEndTouch: (()->Void)?
init(target: Any?, action: Selector?, didBeginTouch: (()->Void)? = nil, didEndTouch: (()->Void)? = nil) {
super.init(target: target, action: action)
self.didBeginTouch = didBeginTouch
self.didEndTouch = didEndTouch
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
self.didBeginTouch?()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
self.didEndTouch?()
}
}
struct TouchesHandler: UIViewRepresentable {
var didBeginTouch: (()->Void)?
var didEndTouch: (()->Void)?
func makeUIView(context: UIViewRepresentableContext<TouchesHandler>) -> UIView {
let view = UIView(frame: .zero)
view.isUserInteractionEnabled = true
view.addGestureRecognizer(context.coordinator.makeGesture(didBegin: didBeginTouch, didEnd: didEndTouch))
return view;
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<TouchesHandler>) {
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator {
@objc
func action(_ sender: Any?) {
print("Tapped!")
}
func makeGesture(didBegin: (()->Void)?, didEnd: (()->Void)?) -> MyTapGesture {
MyTapGesture(target: self, action: #selector(self.action(_:)), didBeginTouch: didBegin, didEndTouch: didEnd)
}
}
typealias UIViewType = UIView
}
struct TestCustomTapGesture: View {
var body: some View {
Text("Hello, World!")
.padding()
.background(Color.yellow)
.overlay(TouchesHandler(didBeginTouch: {
print(">> did begin")
}, didEndTouch: {
print("<< did end")
}))
}
}
struct TestCustomTapGesture_Previews: PreviewProvider {
static var previews: some View {
TestCustomTapGesture()
}
}
您可以将此手势添加到您的视图中:
.onLongPressGesture(minimumDuration: 100.0, maximumDistance: .infinity, pressing: { pressing in
if pressing {
print("My long pressed starts")
} else {
print("My long pressed ends")
}
}, perform: { })