UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: ForEach bindings

Forums > SwiftUI

The following code creates a table-type structure, and the goal is for a particular, tapped box (not all the boxes) in the grid to change from an empty rectangle with a red border to a rectangle containing text with a white/black border. I could swear I got this to work before I slimmed down the code and moved the ZStack{}s into a separate struct. Then I tried to reverse engineer the original code because I forgot to commit it. But maybe I was hallucinating. 😵‍💫 Right now, tapping a box correctly displays text in only that box, but all the borders are changing from red to white/black.

(Item is a struct, with an embedded struct Box; subarray1 and subarray2 are each an Array<Box> inside Item.)

Was I hallucinating? Or am I missing/misplacing a key word somewhere? What appears below should be identical or nearly identical to the original code I had. It has to be somewhere in the commented lines, as those are what had to be adjusted when I moved the ZStack to its own struct.

struct LowerPane: View {
    @State var currentItem: Item
    @State private var borderColor = Color.red  // changed

    var body: some View {
        GeometryReader { ruler in
            ScrollView {
                HStack(spacing: 0) {
                    VStack(spacing: 0) {
                        ForEach($currentItem.subarray1) { $box in
                            ZStack {
                                Rectangle()
                                    .fill(.primary).colorInvert()
                                    .frame(width: ruler.size.width * 0.5, height: 60, alignment: .center)
                                    .border(borderColor)
                                    .onTapGesture(perform: {
                                        box.selected = true  // changed
                                        borderColor = Color.primary  // changed
                                    })
                                if box.selected {  // changed
                                    MyText(str: box.statement)  // changed
                                }
                            }
                        }
                    }

                    VStack(spacing: 0) {
                        ForEach($currentItem.subarray2) { $step in
                            ZStack {
                                Rectangle()
                                    .fill(.primary).colorInvert()
                                    .frame(width: ruler.size.width * 0.5, height: 60, alignment: .center)
                                    .border(borderColor)
                                    .onTapGesture(perform: {
                                        box.selected = true  // changed
                                        borderColor = Color.primary  // changed
                                    })
                                if box.selected {  // changed
                                    MyText(str: box.statement)  // changed
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

2      

All of your boxes have a border color modifier controlled by the same property:

.border(borderColor)

So if you change borderColor for one box then all of them will change color.

You should do something like this instead:

.border(box.selected ? .red : .primary)

2      

Ah, okay--thanks!

Did my "compacted" version (below) work (changing the color of the tapped box only) because the ForEach creates x number of LowerBox views (and each view has its own borderColor), whereas my "uncompacted" version creates x number of Rectangle shapes?

Either way, I can eliminate the need for the borderColor variable thanks to your advice, @roosterboy. 🌟

struct LowerPane: View {
    @State var currentItem: Item

    var body: some View {
        GeometryReader { ruler in
            ScrollView {
                HStack(spacing: 1) {
                    VStack(spacing: 1) {
                        ForEach($currentItem.subarray1) { $box in
                            LowerBox(r: ruler, s: box)
                        }
                    }

                    VStack(spacing: 1) {
                        ForEach($currentItem.subarray2) { $box in
                            LowerBox(r: ruler, s: box)
                        }
                    }

                }
            }
        }
    }
}

struct LowerBox: View {
    var r: GeometryProxy
    @State var s: Item.Box

    @State private var borderColor = Color.red

    var body: some View {
        ZStack {
            Rectangle()
                .fill(.primary).colorInvert()
                .frame(width: r.size.width * 0.5, height: 60, alignment: .center)
                .border(borderColor)
                .onTapGesture(perform: {
                    s.selected = true
                    borderColor = Color.primary
                })
            if s.selected {
                MyText(str: s.statement)
            }
        }
    }
}

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.