I created a simple view designed to show the content of an array that is stored in an Observable Object property:
struct Satellite {
let name: String
let orbit: Double
let color: Color
}
class OrbitalSystem: ObservableObject{
@Published var objects: [Satellite] = []
}
struct OrbitalSystemView: View {
@ObservedObject var orbitalSystem: OrbitalSystem
var body: some View{
VStack{
ForEach(0..<orbitalSystem.objects.count){ n in
HStack{
Text("(orbitalSystem.objects[n].name)")
Spacer()
Text("(orbitalSystem.objects[n].orbit, specifier: "%.2f")")
}
.padding()
.background(orbitalSystem.objects[n].color)
}.padding()
}
}
}
Then I want to use this general view in ContentView, however in the App I'm using a different struct for the data (struct Planet) than the one used in the general view (struct Satellite). In order to keep the required changes minimal I added a computed property to the struct Planet converting the content of Planet to an element Satellite. With any change to my array of Planets in ContentView a property observer converts it to an array of Satellites and stores it in the ObservedObject property. To update the view when initially loaded the Array of Planets is set by using the .onAppear modifier.
struct Planet {
let name: String
let distanceToSun: Int
let surface: Color
var satellite : Satellite {
Satellite(name: self.name, orbit: Double(self.distanceToSun), color: self.surface)
}
}
struct ContentView: View {
@ObservedObject var planetarySystem = OrbitalSystem()
@State private var planets: [Planet] = []{
didSet{
var objects = [Satellite]()
for planet in planets {
objects.append(planet.satellite)
}
planetarySystem.objects = objects
}
}
var body: some View {
OrbitalSystemView(orbitalSystem: planetarySystem)
.onAppear{
planets = [
Planet(name: "Mercury", distanceToSun: 1, surface: Color.orange),
Planet(name: "Venus", distanceToSun: 2, surface: Color.gray),
Planet(name: "Earth", distanceToSun: 3, surface: Color.blue),
Planet(name: "Mars", distanceToSun: 4, surface: Color.red)
]
}
}
}
This code works in principle but has a very strange behavior. My expected result would be: A Stack of four lines listing: Mercury 1.00, Venus 2.00, Earth 3.00, Mars 4.00 with different colors in the background
however with the code above I get an empty view. I get the expected result by modifying the the OrbitalSystem Class like this:
class OrbitalSystem: ObservableObject{
@Published var objects: [Satellite] = [
Satellite(name: "", orbit: 0, color: Color.white),
Satellite(name: "", orbit: 0, color: Color.white),
Satellite(name: "", orbit: 0, color: Color.white),
Satellite(name: "", orbit: 0, color: Color.white)
]
}
if
@Published var objects: [Satellite] = [
Satellite(name: "", orbit: 0, color: Color.white),
Satellite(name: "", orbit: 0, color: Color.white)
]
is changed to this the resulting view also shows only two rows: Mercury 1.00 and Venus 2.00 with the respective background colors.
It therefore seems the .count in the ForEach(0..<orbitalSystem.objects.count) returns the wrong value even though the content of the array has been updated.
I'm using Xcode 12.3
I'd be grateful for any clarification on this behavior. Thanks