当 SwiftUI Picker 选择更改 EnvironmentObject 时,有没有办法调用函数?
Is there a way to call a function when a SwiftUI Picker selection changes an EnvironmentObject?
我有一个正在使用的 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[[=10=]])")
}
}.pickerStyle(SegmentedPickerStyle())
}
}
}
在此处查看了这个类似(但不同)的问题后: 我已尝试如下使用 onReceive(),但当 Slider 值更改时也会调用它,从而导致不需要的行为。
.onReceive([self.valueHolder].publisher.first(), perform: {_ in
self.segmentedControlFunction()
})
我已经尝试更改 onReceive 的参数以仅按该值过滤它。传递的值是正确的,但 segmentedControlFunction 在滑块移动时仍会被调用,而不仅仅是在选择器发生变化时。
.onReceive([self.valueHolder.segmentedControlValue].publisher.first(), perform: {_ in
self.segmentedControlFunction()
})
如何让 segmentedControlFunction 以类似于 sliderFunction 的方式被调用?
当我验证原始问题并修改我的代码时,我无意中发现了我认为可能是最佳解决方案。所以,如果它能帮助其他人,这里是:
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[[=10=]])")
}
}.pickerStyle(SegmentedPickerStyle())
.onReceive([self._segmentedControlValue].publisher.first(), perform: {_ in
if self.segmentedControlValueDidChange {
self.segmentedControlFunction()
}
})
}
}
}
请注意,onReceive() 用于新的@State 属性,它与 Bool 配对,评估它是否与 @EnvironmentObject 对应物相同。然后在函数调用时更新@State 值。另请注意,在解决方案中,我更改了 sliderFunction 的工作方式,以便在 @EnvironmentObject 值实际更改时仅调用一次。这有点 hacky,但是 Slider 和 Picker 在更新值和调用它们各自的函数时都以相同的方式工作。
有更简单的方法,看起来更适合我。
通用架构如下
Picker("Label", selection: Binding( // proxy binding
get: { self.viewModel.value }, // get value from storage
set: {
self.viewModel.value = [=10=] // set value to storage
self.anySideEffectFunction() // didSet function
}) {
// picker content
}
我有一个正在使用的 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[[=10=]])")
}
}.pickerStyle(SegmentedPickerStyle())
}
}
}
在此处查看了这个类似(但不同)的问题后:
.onReceive([self.valueHolder].publisher.first(), perform: {_ in
self.segmentedControlFunction()
})
我已经尝试更改 onReceive 的参数以仅按该值过滤它。传递的值是正确的,但 segmentedControlFunction 在滑块移动时仍会被调用,而不仅仅是在选择器发生变化时。
.onReceive([self.valueHolder.segmentedControlValue].publisher.first(), perform: {_ in
self.segmentedControlFunction()
})
如何让 segmentedControlFunction 以类似于 sliderFunction 的方式被调用?
当我验证原始问题并修改我的代码时,我无意中发现了我认为可能是最佳解决方案。所以,如果它能帮助其他人,这里是:
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[[=10=]])")
}
}.pickerStyle(SegmentedPickerStyle())
.onReceive([self._segmentedControlValue].publisher.first(), perform: {_ in
if self.segmentedControlValueDidChange {
self.segmentedControlFunction()
}
})
}
}
}
请注意,onReceive() 用于新的@State 属性,它与 Bool 配对,评估它是否与 @EnvironmentObject 对应物相同。然后在函数调用时更新@State 值。另请注意,在解决方案中,我更改了 sliderFunction 的工作方式,以便在 @EnvironmentObject 值实际更改时仅调用一次。这有点 hacky,但是 Slider 和 Picker 在更新值和调用它们各自的函数时都以相同的方式工作。
有更简单的方法,看起来更适合我。
通用架构如下
Picker("Label", selection: Binding( // proxy binding
get: { self.viewModel.value }, // get value from storage
set: {
self.viewModel.value = [=10=] // set value to storage
self.anySideEffectFunction() // didSet function
}) {
// picker content
}