为什么按钮在嵌入 SwiftUI 表单时不起作用?

jar*_*aez 20 swiftui

我正在构建一个 SwitUI 表单。我需要绘制一些自定义滑块。为此,我创建了一个视图 (SliderView),并将其多次包含在表单中。我花了一些时间来管理 Binding 以在父视图中获取 Sliders 的值,但我能够做到 :-) 我无法理解的最大谜团是为什么我放置在孩子中的按钮视图,当它们嵌入到表单中时不起作用。它们在 VStack 中工作正常。这里的代码:

//  SliderView.swift

import SwiftUI

struct SliderView: View {
    var name: String
    var min: Double
    var max: Double
    var step: Double
    var defaultValue: Double

    let numberFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 2
        formatter.minimumIntegerDigits = 1
        return formatter
    }()


    @Binding var selectedValue: Double
    var body: some View {
        HStack{
            Text("\(name)")
            Slider(value: $selectedValue, in: self.min...self.max, step: self.step)
            Button(action: {
                if (self.selectedValue - self.step) >= self.min{
                    self.selectedValue = self.selectedValue - self.step
                }
            }) {
                Image(systemName:"minus.circle")
            }
            Text("\(numberFormatter.string(from: NSNumber(value: selectedValue)) ?? "")")
                .padding(5)
                .onAppear {
                    self.selectedValue = self.defaultValue
                }
            Button(action: {
                if (self.selectedValue + self.step) <= self.max{
                    self.selectedValue = self.selectedValue + self.step
                }
            }) {
                Image(systemName:"plus.circle")
            }


        }

    }
}

struct SliderView_Previews: PreviewProvider {
    @State static var myValue: Double = 1
    static var previews: some View {
        SliderView(name: "CPU", min: 1, max: 10, step: 0.5, defaultValue: 1, selectedValue: $myValue)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我正在构建的表单的一部分。代码编译,滑块工作,每个滑块的值都可以在父视图中看到。但是,逐步更新滑块的按钮不起作用(因为它们嵌入在表单中)

//  SliderView.swift

import SwiftUI

struct SliderView: View {
    var name: String
    var min: Double
    var max: Double
    var step: Double
    var defaultValue: Double

    let numberFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 2
        formatter.minimumIntegerDigits = 1
        return formatter
    }()


    @Binding var selectedValue: Double
    var body: some View {
        HStack{
            Text("\(name)")
            Slider(value: $selectedValue, in: self.min...self.max, step: self.step)
            Button(action: {
                if (self.selectedValue - self.step) >= self.min{
                    self.selectedValue = self.selectedValue - self.step
                }
            }) {
                Image(systemName:"minus.circle")
            }
            Text("\(numberFormatter.string(from: NSNumber(value: selectedValue)) ?? "")")
                .padding(5)
                .onAppear {
                    self.selectedValue = self.defaultValue
                }
            Button(action: {
                if (self.selectedValue + self.step) <= self.max{
                    self.selectedValue = self.selectedValue + self.step
                }
            }) {
                Image(systemName:"plus.circle")
            }


        }

    }
}

struct SliderView_Previews: PreviewProvider {
    @State static var myValue: Double = 1
    static var previews: some View {
        SliderView(name: "CPU", min: 1, max: 10, step: 0.5, defaultValue: 1, selectedValue: $myValue)
    }
}
Run Code Online (Sandbox Code Playgroud)

只需在上面的代码中用“VStack”替换“Form”,按钮就可以正常工作。任何人都可以透露一些信息吗?我认为是关于表单中不同层的问题。当我尝试单击按钮时,似乎单击了带有滑块的整行,但没有任何反应。我在 MacOS 10.15 beta (19A558d) 中使用 Xcode 11 GM 提前致谢!

pbc*_*pbc 20

据我所知,当表单行包含接受点击手势的视图(例如按钮)时,它们的行为会有所不同。当该行包含一个 Button 时,当用户点击该按钮时,同一行中的所有其他内容也会收到点击手势。所以在你上面的例子中,并不是你的按钮不起作用,实际发生的是它们都在接收点击手势并执行它们的动作,但是由于你的代码中的逻辑有效地取消了另一个的效果,它看起来好像它们不工作。

为了帮助证明这一点,这里是我写的一些测试代码:

    @State var count1 = 0
    @State var count2 = 0

    func buttonTest() -> some View {
        HStack {
            Button(action: {
                self.count1 += 1
            }) {
                Text("Button 1: \(count1)")
            }
            Spacer()
            Button(action: {
                self.count2 += 1
            }) {
                Text("Button 2: \(count2)")
            }
        }
    }

    var body: some View {
        VStack {
            buttonTest()
            Form {
                Section(header: Text("Test")) {
                    buttonTest()
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果您点击表单上方的任一按钮,它们会按预期独立工作,仅增加自己的计数,但如果您点击表单中的任一按钮或行中的任何其他位置,则两个按钮的操作都会执行,并且两个计数都会增加。

我不确定 Apple 为什么要这样做,但我也在努力寻找解决此问题的方法。

编辑

感谢Shauket Sheikh,有一个解决方案,但它使代码重用不太优雅。

所以当Button在Form中时,需要将代码放入.onTapGesturehandler中,但是如果Button在Form之外,则需要action:handler中的代码。

我真的希望这不是 Apple 对 Forms 的长期立场:它违背了一次编写,到处使用的理念。


小智 15

尝试向表单中的按钮添加一个 buttonStyle 修饰符。这对我有用:

Button(action: {}, label: {Text("Test")})
    ­­­­­.buttonStyle(PlainButtonStyle())
Run Code Online (Sandbox Code Playgroud)