当SwiftUI Picker选择更改EnvironmentObject时,是否可以调用函数?

时间:2020-04-30 07:16:58

标签: ios swift slider swiftui picker

我有一个正在使用的SwiftUI表单,用于更新EnvironmentObject的值。当Picker的值更改时,我需要调用一个函数,但是我发现的每一种方式都确实很混乱,或导致其他问题。我正在寻找一种类似的Slider工作方式,但似乎没有。没有任何解决方案的基本代码在这里:

class ValueHolder : ObservableObject {

@Published var sliderValue : Float = 0.5
static let segmentedControlValues : [String] = ["one", "two", "three", "four", "five"]
@Published var segmentedControlValue : Int = 3

}
struct ContentView: View {

    @EnvironmentObject var valueHolder : ValueHolder

    func sliderFunction() {
        print(self.valueHolder.sliderValue)
    }
    func segmentedControlFunction() {
        print(ValueHolder.segmentedControlValues[self.valueHolder.segmentedControlValue])
    }

    var body: some View {
        Form {
            Text("\(self.valueHolder.sliderValue)")
            Slider(value: self.$valueHolder.sliderValue, onEditingChanged: {_ in self.sliderFunction()
            })
            Text("\(ValueHolder.segmentedControlValues[self.valueHolder.segmentedControlValue])")
            Picker("", selection: self.$valueHolder.segmentedControlValue) {
                ForEach(0..<ValueHolder.segmentedControlValues.count) {
                    Text("\(ValueHolder.segmentedControlValues[$0])")
                }
            }.pickerStyle(SegmentedPickerStyle())
        }
    }
}

在这里检查了类似(但不同)的问题后:Is there a way to call a function when a SwiftUI Picker selection changes?我尝试如下使用onReceive(),但是当Slider值更改时,也会调用它,从而导致不良行为。

.onReceive([self.valueHolder].publisher.first(), perform: {_ in
                self.segmentedControlFunction()
            })

我尝试更改onReceive的参数以仅通过该值对其进行过滤。传递的值是正确的,但是在滑块移动时(不仅是在选择器发生更改时),仍然会调用segmentedControlFunction。

.onReceive([self.valueHolder.segmentedControlValue].publisher.first(), perform: {_ in
                self.segmentedControlFunction()
            })

如何以与sliderFunction类似的方式来调用segmentedControlFunction?

2 个答案:

答案 0 :(得分:5)

有一种更简单的方法,对我来说似乎更合适。

通用模式如下

Picker("Label", selection: Binding(    // proxy binding
    get: { self.viewModel.value },     // get value from storage
    set: {
            self.viewModel.value = $0  // set value to storage

            self.anySideEffectFunction() // didSet function
    }) {
       // picker content
    }

答案 1 :(得分:0)

当我在验证原始问题并修改代码时,偶然发现了我认为可能是最佳解决方案的地方。因此,如果它可以帮助其他人,这里是:

struct ContentView: View {

    @EnvironmentObject var valueHolder : ValueHolder

    @State private var sliderValueDidChange : Bool = false
    func sliderFunction() {
        if self.sliderValueDidChange {
            print("Slider value: \(self.valueHolder.sliderValue)\n")
        }
        self.sliderValueDidChange.toggle()
    }

    var segmentedControlValueDidChange : Bool {
        return self._segmentedControlValue != self.valueHolder.segmentedControlValue
    }
    @State private var _segmentedControlValue : Int = 0
    func segmentedControlFunction() {
        self._segmentedControlValue = self.valueHolder.segmentedControlValue
        print("SegmentedControl value: \(ValueHolder.segmentedControlValues[self.valueHolder.segmentedControlValue])\n")
    }


    var body: some View {
        Form {
            Text("\(self.valueHolder.sliderValue)")
            Slider(value: self.$valueHolder.sliderValue, onEditingChanged: {_ in self.sliderFunction()
            })
            Text("\(ValueHolder.segmentedControlValues[self.valueHolder.segmentedControlValue])")
            Picker("", selection: self.$valueHolder.segmentedControlValue) {
                ForEach(0..<ValueHolder.segmentedControlValues.count) {
                    Text("\(ValueHolder.segmentedControlValues[$0])")
                }
            }.pickerStyle(SegmentedPickerStyle())
                .onReceive([self._segmentedControlValue].publisher.first(), perform: {_ in
                    if self.segmentedControlValueDidChange {
                        self.segmentedControlFunction()
                    }
            })
        }
    }
}

请注意,onReceive()用于新的@State属性,该属性与Bool配对以评估其是否与@EnvironmentObject对应项相同。然后,在函数调用中更新@State值。还要注意,在解决方案中,我更改了sliderFunction的工作方式,以便在@EnvironmentObject值实际更改时仅调用一次。有点黑,但是在更新值和调用它们各自的函数时,Slider和Picker的工作方式相同。