在SwiftUI中按下按钮时如何检测并采取措施 [英] How to detect and take action when a button is being pressed in SwiftUI
问题描述
我想在开始按下按钮然后停止按下按钮时执行一个动作.我一直在寻找一个简单的解决方案,但进入了更复杂的配置.一个非常简单且接近的选项是我从 BlueSpud 获得的.不使用按钮操作,所以我尝试了:
I want to execute an action when the button press begins and then when the button stops being pressed. I was looking for a simple solution, but got into more complicated configurations. One option that is pretty simple and close is the one I got from BlueSpud. The button action is not used so I tried:
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,并且如果事件没有可靠的结束,则该解决方案将不起作用.
The problem with this code is that if you drag your finger out of the button area while pressing, .onEnded never gets called, and without a reliable end to the event, the solution doesn't work.
我还尝试了Apple的编写SwiftUI手势的示例.它提供了对按下状态和未按下状态的非常一致的控制,但是我似乎不知道在何处插入动作:
I have also tried Apple's example for composing SwiftUI gestures. It provides a very consistent control over the pressed and unpressed states, but I can't seem to know where to insert my actions:
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)
}
}
推荐答案
一旦本机SwiftUI不允许您现在想要实现的目标,我将建议采用以下方法,该方法有效且可管理,因此可靠
As soon as native SwiftUI does not allow now what you want to achieve, I'd recommend the following approach, which is valid and manageable and, so, reliable.
该演示显示了基于 UIGestureRecongnizer
/ UIViewRepresentable
的简化代码,该代码可以轻松扩展(例如,如果要拦截 touchesCanceled
,点击计数等)
The demo shows simplified code based on using UIGestureRecongnizer
/UIViewRepresentable
, which can be easily extended (eg. if you want to intercept touchesCanceled
, click count, etc.)
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()
}
}
这篇关于在SwiftUI中按下按钮时如何检测并采取措施的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!