(This post follows on from the solution to Untraceable SIGABRT: precondition failure: attribute failed to set an initial value: <num>)
Hopefully one of the last posts I make as I sprint toward this project's end. The issue involves a lot of code that wouldn't be practical to paste here, so I'll do my best to simplify some and leave other bits out completely. If you need the full thing I could maybe email the relevant files but for now hopefully this will do.
I have a Dots(_:) view which takes a single parameter, quantity. There is then a chunk of complicated looking code that ultimately randomly places them on screen - this bit has been left out of the post.
struct Dots: View {
@State private var positions: [DotPosition] = []
let quantity: Int
let dot: some View = Circle()
.size(width: diam, height: diam)
.fill(Color.red)
init(_ quantity: Int) {
self.quantity = quantity
self._positions = State(initialValue: createPositions())
for pos in positions { print(pos)} // <- this is showing that new values ARE being made
}
var body: some View {
GeometryReader { geom in
ForEach(self.positions, id: \.self) {position in
self.dot
.offset(position.offset(maxWidth: geom.size.width, maxHeight: geom.size.height))
// ^ a print statement in here shows iteration over the new values, and then the very first values
}
}
}
}
The file that could effectively be used as ContentView/root for this problem, again simplified, is
struct SwipableEquation: View {
let num: Int
var body: some View {
VStack {
GeometryReader { geo in
HStack(spacing: 0) {
Group {
Dots(self.num) // <- prints show a new instance gets created when exepcted, but the view doesn't change
}
}
}
Text(String(num)) // <- this value actively changes as and when expected
}
}
}
struct ContentView: View {
var nums: [Int] = [2,3,4]
@State private var currentEqn = 0
var body: some View {
VStack {
SwipableEquation(num: nums[currentEqn])
HStack {
// Previous Eqn
Button(action: {
self.currentEqn -= 1
print("\n######### BACK #########")
}) {
Image(systemName: "chevron.left")
VStack {
Text("Previous Equation")
if currentEqn > 0 {
Text(String(nums[currentEqn-1]))
}
}
}.disabled(self.currentEqn == 0)
Spacer()
// Next eqn
Button(action: {
self.currentEqn += 1
print("\n######### NEXT #########")
}) {
VStack {
Text("Next Equation")
if currentEqn < nums.count-1 {
Text(String(nums[currentEqn+1]))
}
}
Image(systemName: "chevron.right")
}.disabled(self.currentEqn == nums.count-1)
}.padding()
}
}
}
You should better include the link to your old thread.
Very interesting behavior.
As far as I tested, it is a problem of `@State` variables.
Please try this:
struct Dots: View {
private var positions: [DotPosition] = [] //<- Remove `@State`
var quantity: Int
let dot: some View = Circle()
.size(width: diam, height: diam)
.fill(Color.red)
init(_ quantity: Int) {
self.quantity = quantity
self.positions = createPositions()
}
var body: some View {
GeometryReader { geom in
ForEach(self.positions, id: \.self) {position in
self.dot
.offset(position.offset(maxWidth: geom.size.width, maxHeight: geom.size.height))
}
}
}
}
Many parts of SwiftUI are hidden and we need to guess what's going bihind, but I guess it's related to the strategy of SwiftUI to allocate actual storage of `@State` variables. When there's an `@State` variable with its storage already allocated, SwiftUI continues to use the allocated storage and ignores `initialValue` of `State`.