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