【问题标题】:How to detect and take action when a button is being pressed in SwiftUI如何在 SwiftUI 中按下按钮时检测并采取行动
【发布时间】:2020-04-09 07:09:52
【问题描述】:

我想在按钮开始按下然后停止按下按钮时执行一个动作。我一直在寻找一个简单的解决方案,但进入了更复杂的配置。我从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 的 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)
    }
}

【问题讨论】:

  • 在另一个问题here 上查看我的解决方案。看起来正是您所需要的。

标签: ios button swiftui


【解决方案1】:

一旦原生 SwiftUI 现在不允许您实现您想要实现的目标,我会推荐以下方法,该方法有效且易于管理,因此可靠。

demo展示了基于使用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()
    }
}

【讨论】:

  • 它像时钟一样工作。给这位先生两杯啤酒?
【解决方案2】:

不需要 UIView 和 Representable,

您可以使用内置的 SwiftUI isPressed 属性来配置 Button,如下所示:

import SwiftUI

struct MyButtonStyle: ButtonStyle
{
    func makeBody(configuration: Configuration) -> some View
    {
        if(configuration.isPressed)
        {
            print("Button is pressed")
        }
        else
        {
            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())
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多