2

I'm using a ForEach to parse a list of models and create a view for each of them, Each view contains a Button and a Text, the Button toggles a visibility state which should hide the text and change the Button's title (Invisible/Visible).

struct ContentView: View {

    var colors: [MyColor] = [MyColor(val: "Blue"), MyColor(val: "Yellow"), MyColor(val: "Red")]

    var body: some View {

        ForEach(colors, id: \.uuid) { color in
            ButtonColorView(color: color.val)
        }

    }
}

struct ButtonColorView: View {

    var color: String

    @State var visible = true

    var body: some View {

        if visible {
            return AnyView(  HStack {
                Button("Invisible") {
                    self.visible.toggle()
                }
                Text(color)
            })
        } else {
            return AnyView(
                Button("Visible") {
                    self.visible.toggle()
                }
            )
        }
    }
}



class MyColor: Identifiable {
    let uuid = UUID()
    let val: String

    init(val: String) {
        self.val = val
    }
}

Unfortunately it's not working, the views inside the ForEach do not change when the Button is pressed. I replaced the Foreach with ButtonColorView(color: colors[0].val) and it seems to work, so I'd say the problem is at ForEach.

I also tried breakpoints in ButtonColorView and it seems the view is called when the Button is triggered returning the right view, anyways the view does not update on screen.

So, am I using the ForEach in a wrong way ?

enter image description here

This problem occurs in a more complex app, but I tried to extract it in this small example. To summarize it: I need ButtonColorView to return different Views depending of its state (visibility in this case)

PS: I'm using Xcode 11 Beta 6

5
  • Could you please share the initializer of ButtonColorView with the color property? Commented Aug 28, 2019 at 7:29
  • @ΒασίληςΔ. There is no initializer for ButtonColorView because it's a struct and I use the default init: ButtonColorView(color: color.val). This is the whole code so you can copy it in Xcode if you wanna test it Commented Aug 28, 2019 at 7:44
  • You created a struct object of type ContentView, that inherits the View. View Struct is not in Foundation framework so we don't have the View Struct so we can't test it. The initializer View(color: MyColor) is in your View Struct file whitch you haven't posted. Commented Aug 28, 2019 at 7:52
  • 1
    @ΒασίληςΔ. View is in SwiftUI framework, as I mentioned in the title this is a SwiftUI app ... Commented Aug 28, 2019 at 8:04
  • Try updating the color on the main thread? Commented Aug 28, 2019 at 9:05

1 Answer 1

1

You are using ForEach correctly. I think it's the if statement within ButtonColorView's body that's causing problems. Try this:

struct ButtonColorView: View {

    var color: String

    @State var visible = true

    var body: some View {
        HStack {
            Button(visible ? "Invisible" : "Visible") {
                self.visible.toggle()
            }
            if visible {
                Text(color)
            }
        }
    }
}

You can also try something like this:

struct ButtonColorView: View {

    var color: String

    @State var visible = true

    var body: some View {
        HStack {
            if visible {
                HStack {
                    Button("Invisible") {
                        self.visible.toggle()
                    }
                    Text(color)
                }
            } else {
                Button("Visible") {
                    self.visible.toggle()
                }
            }
        }
    }

}
Sign up to request clarification or add additional context in comments.

4 Comments

As I said this is a small example to ilustrate a problem that accurs in a complex app. Your solution solves the problem in this particular case, unfortunately I can't apply it in my app, the problem is that I need ButtonColorView to return a different View depending of its state, it may be a Text, Section or EmptyView. In your solution ButtonColorView always returns a HStack which makes it working, but in the real case I can't wrap everything in a HStack. Thank you!
The problem definitely is with the if statement within ButtonColorView's body. Maybe you can share more of your actual project - it would make it easier to help.
The actual ButtonColorViewlooks like this var body: some View { let props = model.properties let view = "Whatever view" if props.visible { return AnyView(view) } else { return AnyView(EmptyView()) } } so I can't remove the if and I can't add the HStack either bacause it breaks by design :(
If I use List instead of ForEach it seems to work just fine, but I do not need a list

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.