Есть ли способ вызвать функцию, когда выбор SwiftUI Picker изменяет EnvironmentObject? - PullRequest
1 голос
/ 30 апреля 2020

У меня есть форма SwiftUI, с которой я работаю, которая обновляет значения EnvironmentObject. Мне нужно вызывать функцию, когда значение Picker меняется, но каждый найденный способ либо беспорядочный, либо вызывает другие проблемы. То, что я ищу, - это похожий способ работы Slider, но, похоже, его нет. Базовый c код без какого-либо решения находится здесь:

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 изменяется? Я пытался использовать 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?

Ответы [ 2 ]

2 голосов
/ 30 апреля 2020

Существует гораздо более простой подход, который выглядит более подходящим для меня.

Общая схема c выглядит следующим образом

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 голосов
/ 30 апреля 2020

Когда я проверял исходный вопрос и переделывал свой код, я случайно наткнулся на то, что я считаю, вероятно, лучшим решением. Итак, на случай, если это кому-нибудь поможет, вот оно:

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 работают одинаково при обновлении значений и вызове их соответствующих функций.

...