2

I'm trying to have multiple expandable views with animation inside a VStack. I have the following code:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    ExpandableView(headerTitle: "First")
                    ExpandableView(headerTitle: "Second")
                    Spacer()
                }
            }
        }
    }
}

And the ExpandableView:

struct ExpandableView: View {
    let headerTitle: String
    @State private var collapsed: Bool = true
    
    var body: some View {
        Button(
            action: {
                self.collapsed.toggle()
            },
            label: {
                VStack(spacing: 2) {
                    ZStack {
                        Rectangle()
                            .fill(.gray)
                        VStack {
                            Text("\(headerTitle) Header")
                            if !collapsed {
                                HStack {
                                    Text("Text A")
                                    Text("Text B")
                                }
                            }
                        }
                    }
                    .frame(height: collapsed ? 52 : 80)
                    ZStack(alignment: .top) {
                        Rectangle()
                            .fill(.gray)
                            .frame(height: 204)
                        VStack {
                            Text("Content A")
                            Text("Content B")
                            Text("Content C")
                        }
                    }
                    .frame(maxHeight: collapsed ? 0 : .none)
                    .clipped()
                }
            }
        )
        .buttonStyle(PlainButtonStyle())
        .animation(.easeOut, value: collapsed)
    }
}

The result is this: enter image description here

As you can see if I open the last expandableView is opens correctly. However if I open the first one when the second is closed, it actually opens the second. It only opens correctly the first one if the second is already open. It seems the VStack is not rendering correctly itself. Any ideas why this happening? Thanks for the help.

1

1 Answer 1

2

I migth be the way the buttons works. Here is a cleaner solution:

struct ExpandableView: View {
        let headerTitle: String
        @State private var collapsed: Bool = true
        
        var body: some View {
            
            VStack(spacing: 2) {
                
                Button(
                    action: {
                        withAnimation(.easeOut){
                            self.collapsed.toggle()
                        }
                    },
                    label: {
                        
                        VStack {
                            Text("\(headerTitle) Header")
                            if !collapsed {
                                HStack {
                                    Text("Text A")
                                    Text("Text B")
                                }
                            }
                        }.frame(maxWidth: .infinity)
                    })
                .buttonStyle(.borderedProminent)
                .tint(.gray)
                
                if(!self.collapsed) {
                    VStack {
                        Divider().background(.black)
                        Text("Content A")
                        Text("Content B")
                        Text("Content C")
                    }
                }
                Spacer()
            }
            .frame(height: collapsed ? 52 : 204)
            .frame(maxWidth: .infinity)
            .background(.gray)
            .padding()
        }
    }
Sign up to request clarification or add additional context in comments.

2 Comments

Absolutely correct answer. But I don't understand why, I see I have to be careful with the button animations. I tried different versions, even removing the button and add a .onTapGesture in the VStack of the header, which happens another strange thing, the tap is not recognized in the VStack but only if tapped in the Text. Will select your answer as correct, thank you!
To fix that tapable area just do .contentShape(Rectangle())

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.