I have what I thought was a simple task, but it appears otherwise. As part of my code, I am simply displaying some circles and when the user clicks on one, it will navigate to a new view depending on which circle was pressed.
If I hard code the views in the NavigationLink, it works fine, but I want to use a dynamic array. In the following code, it doesn't matter which circle is pressed, it will always display the id of the last item in the array; ie it passes the last defined dot.destinationId to the Game view
struct Dot: Identifiable {
let id = UUID()
let x: CGFloat
let y: CGFloat
let color: Color
let radius: CGFloat
let destinationId: Int
}
struct ContentView: View {
var dots = [
Dot(x:10.0,y:10.0, color: Color.green, radius: 12, destinationId: 1),
Dot(x:110.0,y:10.0, color: Color.green, radius: 12, destinationId: 2),
Dot(x:110.0,y:110.0, color: Color.blue, radius: 12, destinationId: 3),
Dot(x:210.0,y:110.0, color: Color.blue, radius: 12, destinationId: 4),
Dot(x:310.0,y:110.0, color: Color.red, radius: 14, destinationId: 5),
Dot(x:210.0,y:210.0, color: Color.blue, radius: 12, destinationId: 6)]
var lineWidth: CGFloat = 1
var body: some View {
NavigationView {
ZStack
Group {
ForEach(dots){ dot in
NavigationLink(destination: Game(id: dot.destinationId))
{
Circle()
.fill(dot.color)
.frame(width: dot.radius, height: dot.radius)
.position(x:dot.x, y: dot.y )
}
}
}
.frame(width: .infinity, height: .infinity, alignment: .center )
}
.navigationBarTitle("")
.navigationBarTitleDisplayMode(.inline)
}
}
struct Game: View {
var id: Int
var body: some View {
Text("\(id)")
}
}
I tried to send the actual view as i want to show different views and used AnyView, but the same occurred It was always causing the last defined view to be navigated to.
I'm really not sure what I a doing wrong, any pointers would be greatly appreciated
While it might seem that you're clicking on different dots, the reality is that you have a ZStack
with overlapping views and you're always clicking on the top-most view. You can see when you tap that the dot with ID 6 is always the one actually getting pressed (notice its opacity changes when you click).
To fix this, move your frame
and position
modifiers outside of the NavigationLink
so that they affect the link and the circle contained in it, instead of just the inner view.
Also, I modified your last frame
since there were warnings about an invalid frame size (note the different between width
/maxWidth
and height/maxHeight
when specifying .infinite
).
struct ContentView: View {
var dots = [
Dot(x:10.0,y:10.0, color: Color.green, radius: 12, destinationId: 1),
Dot(x:110.0,y:10.0, color: Color.green, radius: 12, destinationId: 2),
Dot(x:110.0,y:110.0, color: Color.blue, radius: 12, destinationId: 3),
Dot(x:210.0,y:110.0, color: Color.blue, radius: 12, destinationId: 4),
Dot(x:310.0,y:110.0, color: Color.red, radius: 14, destinationId: 5),
Dot(x:210.0,y:210.0, color: Color.blue, radius: 12, destinationId: 6)]
var lineWidth: CGFloat = 1
var body: some View {
NavigationView {
ZStack {
Group {
ForEach(dots){ dot in
NavigationLink(destination: Game(id: dot.destinationId))
{
Circle()
.fill(dot.color)
}
.frame(width: dot.radius, height: dot.radius) //<-- Here
.position(x:dot.x, y: dot.y ) //<-- Here
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center )
}
.navigationBarTitle("")
.navigationBarTitleDisplayMode(.inline)
}
}
}