当SwiftUI Picker选择改变环境对象时,是否有办法调用一个函数?

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

我有一个正在使用的SwiftUI表格,它可以更新一个环境对象的值。当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())
        }
    }
}

在回顾了这个类似的(但不同的)问题后,这里: 有没有办法在SwiftUI Picker选择发生变化时调用一个函数? 我尝试过使用如下的onReceive(),但是当Slider的值发生变化时,它也会被调用,从而导致不必要的行为。

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

我试着改变onReceive的参数,只用该值进行过滤。传递的值是正确的,但分段控制函数仍然在滑块移动时被调用,而不仅仅是在拾取器改变时。

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

我怎样才能让segmentedControlFunction以类似sliderFunction的方式被调用?

ios swift slider swiftui picker
1个回答
3
投票

有一个更简单的方法,看起来更适合我。

通用模式如下

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
    }

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在更新值和调用各自的函数时,工作方式都是一样的。

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