Home > Blockchain >  How to do a "reveal"-style collapse/expand animation in SwiftUI?
How to do a "reveal"-style collapse/expand animation in SwiftUI?

Time:12-18

I'd like to implement an animation in SwiftUI that "reveals" the content of a view to enable expand/collapse functionality. The content of the view I want to collapse and expand is complex: It's not just a simple box, but it's a view hierarchy of dynamic height and content, including images and text.

I've experimented with different options, but it hasn't resulted in the desired effect. Usually what happens is that when I "expand", the whole view was shown right away with 0% opacity, then gradually faded in, with the buttons under the expanded view moving down at the same time. That's what happened when I was using a conditional if statement that actually added and removed the view. So that makes sense.

I then experimented with using a frame modifier: .frame(maxHeight: isExpanded ? .infinity : 0). But that resulted in the contents of the view being "squished" instead of revealed.

I made a paper prototype of what I want:

paper prototype

Any ideas on how to achieve this?

CodePudding user response:

Something like this might work. You can modify the height of what you want to disclose to be 0 when hidden or nil when not so that it'll go for the height defined by the views. Make sure to clip the view afterwards so the contents are not visible outside of the frame's height when not disclosed.

struct ContentView: View {
    @State private var isDisclosed = false
    
    var body: some View {
        VStack {
            Button("Expand") {
                withAnimation {
                    isDisclosed.toggle()
                }
            }
            .buttonStyle(.plain)
            
            
            VStack {
                GroupBox {
                    Text("Hi")
                }
                
                GroupBox {
                    Text("More details here")
                }
            }
            .frame(height: isDisclosed ? nil : 0, alignment: .top)
            .clipped()
            
            HStack {
                Text("Cancel")
                Spacer()
                Text("Book")
            }
        }
        .frame(maxWidth: .infinity)
        .background(.thinMaterial)
        .padding()
    }
}

No, this wasn't trying to match your design, either. This was just to provide a sample way of creating the animation.

  • Related