防止在 SwiftUI 中同时按下多个按钮

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

我有一个 SwiftUI 应用程序,带有复杂的仪表板视图,其中包含其他几个包含 SwiftUI 按钮的嵌套自定义 SwiftUI 视图。

我想防止用多个手指同时按下这些不同的按钮并同时执行它们的操作,这目前是可能的。

我还找不到任何对我有用的解决方案。例如。在这个SO问题中(如何防止在swiftUI中同时点击两个按钮?)有两种解决方案:

  1. 在仪表板视图的

    multitouch
    中禁用
    exclusiveTouch
    并启用
    init

     UIButton.appearance().isMultipleTouchEnabled = false
     UIButton.appearance().isExclusiveTouch = true
    
     UIView.appearance().isMultipleTouchEnabled = false
     UIView.appearance().isExclusiveTouch = true
    

这对我来说绝对不起作用,无论是在视图的

init
中,还是当我将其全局放在
@main
init 或应用程序的其他全局入口点中时。

  1. 另一种方法是具有
    isEnabled
    状态,并在每次按下按钮时切换它,并将所有其他按钮设置为
    disabled
    。但这个解决方案对我来说不起作用,因为实际的 SwiftUI 按钮部分嵌套在其他视图的多个层中。

我无法理解,这在 SwiftUI 中是一个很大的痛苦。以某种方式应该可以防止同时按下多个按钮。

还有其他解决办法吗?

swiftui multi-touch swiftui-button
1个回答
0
投票

如果您利用

EnvironmentObject
来管理仪表板中的所有按钮,那就很容易了。让我们一步步实现该解决方案。

  1. 创建一个枚举来定义按钮的 ID,例如:
enum ButtonId {
    case login
    case signup
    case forgotPassword
}
  1. 创建一个管理器来保留按下的按钮 ID:
final class ButtonsManager: ObservableObject {
    @Published var pressedButtonId: ButtonId?
}
  1. 创建一个能够与按钮管理器通信的
    ButtonStyle
    。当按下/释放按钮时,该结构将告诉经理。为了让您检查按下/释放/启用/禁用按钮之间的差异,我使用了不同的颜色,但这只是一个示例,您可以根据需要自定义样式。
struct ButtonsManagerStyle: ButtonStyle {
    @Environment(\.isEnabled) private var isEnabled: Bool
    @EnvironmentObject private var buttonsManager: ButtonsManager
    let buttonId: ButtonId

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .foregroundColor(.white)
            .contentShape(Rectangle())
            .background(isEnabled ? Color.red.opacity(configuration.isPressed ? 0.3 : 1) : Color.gray.opacity(0.8))
            .onChange(of: configuration.isPressed) { isPressed in
                if isPressed {
                    buttonsManager.pressedButtonId = buttonId
                } else {
                    buttonsManager.pressedButtonId = nil
                }
            }
    }
}
  1. 创建一个
    ViewModifier
    以应用于您的按钮。为了不为仪表板中的所有按钮复制某些逻辑,这将很有用:
struct ButtonsManagerModifier: ViewModifier {
    @EnvironmentObject private var buttonsManager: ButtonsManager
    let buttonId: ButtonId

    func body(content: Content) -> some View {
        content
            .buttonStyle(ButtonsManagerStyle(buttonId: buttonId))
            .disabled(buttonsManager.pressedButtonId != nil && buttonsManager.pressedButtonId! != buttonId)
    }
}
  1. 最后,让我们使用目前为止已经实现的内容:
// MARK: - Root View

struct MyRootView: View {
    @StateObject private var buttonsManager = ButtonsManager()
    var body: some View {
        VStack(spacing: 50) {
            Button {
                print("Login did tap")
            } label: {
                Text("Login")
            }
            .modifier(ButtonsManagerModifier(buttonId: .login))

            SignupNestedView()

            ForgotPasswordNestedView()
        }
        .environmentObject(buttonsManager)
    }
}

// MARK: - Signup

struct SignupNestedView: View {
    var body: some View {
        Button {
            print("Signup did tap")
        } label: {
            Text("Sigup")
        }
        .modifier(ButtonsManagerModifier(buttonId: .signup))
    }
}

// MARK: - Forgot Password

struct ForgotPasswordNestedView: View {
    var body: some View {
        ForgotPasswordNestedNestedView()
    }
}

struct ForgotPasswordNestedNestedView: View {
    var body: some View {
        Button {
            print("Forgot password did tap")
        } label: {
            Text("Forgot Password")
        }
        .modifier(ButtonsManagerModifier(buttonId: .forgotPassword))
    }
}

您所要做的就是在根目录中注入环境对象(无论它是什么),并记住将我们为仪表板中的按钮创建的修改器设置为。结果如下:

© www.soinside.com 2019 - 2024. All rights reserved.