ios 如何在SwiftUI中检测按钮被按下时采取操作

rbl8hiat  于 2022-12-24  发布在  iOS
关注(0)|答案(2)|浏览(228)

我想在按钮开始按下时执行一个操作,然后在按钮停止按下时执行。我一直在寻找一个简单的解决方案,但遇到了更复杂的配置。一个非常简单和接近的选项是我从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,并且如果没有可靠的事件结束,则解决方案将无法工作。
我也尝试过苹果的composing SwiftUI gestures示例,它提供了对按下和未按下状态的非常一致的控制,但我似乎不知道在哪里插入我的操作:

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)
    }
}
k5ifujac

k5ifujac1#

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

t9aqgxwy2#

不需要UI视图和可表示,
您可以使用内置的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()
        }
    }
}

相关问题