1

For a something that would be simple with UIKit, I'm struggling to find the best solution using SwiftUI. I would appreciate any help / guidance.

I have an array of objects, each of which can be selected. My first attempt do display this was:

final class MeasurementSelections: ObservableObject {

    struct Measurement: Identifiable {
        let id = UUID()
        let name: String
        var isSelected: Bool = false

        init(name: String) {
            self.name = name
        }
    }

    @Published var measurements: [Measurement]

    init(measurements: [Measurement]) {
        self.measurements = measurements
    }
}

struct Readings: View {

    @ObservedObject var model: MeasurementSelections

    var body: some View {
        List(0..<model.measurements.count) { index in
            Text(self.model.measurements[index].name)
                .font(.subheadline)
            Spacer()
            Toggle(self.model.measurements[index].name, isOn: self.$model.measurements[index].isSelected)
                .labelsHidden()
        }
    }
}

This is fairly straightforward, but the use of a Range and the replication of self.model.measurements[index] doesn't seem right… I'd rather have a separate View that takes a Measurement as a parameter.

Follow this answer, my next attempt was…

final class MeasurementSelections: ObservableObject {

    struct Measurement: Identifiable {
        let id = UUID()
        let name: String
        var isSelected: Binding<Bool>

        private var selected: Bool = false

        init(name: String) {
            self.name = name

            let selected = CurrentValueSubject<Bool, Never>(false)
            self.isSelected = Binding<Bool>(get: { selected.value }, set: { selected.value = $0 })
        }
    }

    @Published var measurements: [Measurement]

    init(measurements: [Measurement]) {
        self.measurements = measurements
    }
}

struct Readings: View {

    @ObservedObject var model: MeasurementSelections

    var body: some View {
        VStack {
            MeasurementView(measurement: measurement)
        }
    }
}

struct MeasurementView: View {
    let measurement: MeasurementSelections.Measurement
    var body: some View {
        HStack {
            Text(measurement.name)
                .font(.subheadline)
            Spacer()
            Toggle(measurement.name, isOn: measurement.isSelected)
                .labelsHidden()
        }
    }
}

This is nicer as regards the view separation, but now the MeasurementSelections class has become significantly more complicated, and requires Combine.

Is there a solution to this problem that keeps the view separation but with a simpler model?

Additionally, I'd eventually like the MeasurementSelections to expose a bindable(?) Bool property that is set when ANY of its measurements are selected, which might inform your answer

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160

1 Answers1

-1

the use of a Range and the replication of self.model.measurements[index] doesn't seem right…

There is no replication, you manipulating data in your model directly, without any "replication"

import SwiftUI

struct Data: Identifiable {
    let id = UUID()
    var name: String
    var on_off: Bool
}

class Model: ObservableObject {
    @Published var data = [Data(name: "alfa", on_off: false), Data(name: "beta", on_off: false), Data(name: "gama", on_off: false)]
}



struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        List(0 ..< model.data.count) { idx in
            HStack {
                Text(verbatim: self.model.data[idx].name)
                Toggle(isOn: self.$model.data[idx].on_off) {
                    EmptyView()
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

**enter image description here**

user3441734
  • 16,722
  • 2
  • 40
  • 59