如何在 SwiftUI 中检测按下按钮并采取行动

问题描述 投票:0回答:3

我想在开始按下按钮时执行一个操作,然后在停止按下按钮时执行一个操作。我一直在寻找一个简单的解决方案,但遇到了更复杂的配置。我从 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)
    }
}
ios button swiftui
3个回答
7
投票

不需要 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()
        }
    }
}


3
投票

一旦原生 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()
    }
}

0
投票

您可以将此手势添加到您的视图中:

.onLongPressGesture(minimumDuration: 100.0, maximumDistance: .infinity, pressing: { pressing in
                if pressing {
                    print("My long pressed starts")
                } else {
                    print("My long pressed ends")
                }
            }, perform: { })
© www.soinside.com 2019 - 2024. All rights reserved.