Tom Mils
Tom Mils

Reputation: 17

SwiftUI How to make a slide in view that pushes another view away while keeping it the same size

Before Slide
After Slide

struct SlideView: View {
        @State var isClicked = false
        var body: some View {
            HStack{
                Rectangle()
                    .fill(.gray)
                    .ignoresSafeArea()
                    .frame(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0)
                
                
            VStack{
                Text("This gets squished")
                HStack{
                    Button{
                        withAnimation(.spring()) {
                            isClicked.toggle()
                        }
                        
                    } label: {
                        Image(systemName: "menucard.fill")
                            .padding(.leading)
                    }
                    Spacer()
                }
                Spacer()
            }

        }
    }
}

So I have this view that kind of "slides in" and it's pretty close to what I want but not exactly, the view that slides in ends up squishing the other view and compressing it to be about 25% of it's size. Of course this makes sense because it's all in a HStack and I end up taking 75% of the HStack's space but I was wondering if anybody had an idea of how I could do something like this but without squishing the second view that gets pushed away? So basically keep it the same size and just have the first 1/4 of the view visible and the other 3/4 just be gone I guess.

Upvotes: 1

Views: 891

Answers (2)

Ranoiaetep
Ranoiaetep

Reputation: 6637

Personally, I think it would make more sense to just use .offset, and put them in a ZStack:

ZStack {
    VStack {
        HStack {
            Spacer()
            Text("yellow")
            Spacer()
        }
        Spacer()
    }
    .background(.yellow)
    
    VStack {
        Text("red")
        HStack {
            Button{
                withAnimation(.spring()) {
                    isClicked.toggle()
                }
            } label: {
                Image(systemName: "menucard.fill")
                    .padding(.leading)
            }
            Spacer()
        }
        Spacer()
    }
    .background(.red)
    .offset(.init(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0, height: 0))
}

And you can add another .offset modifier to the yellow one to make it looks slidein:

VStack {
  //...
}
.background(.yellow)
.offset(.init(width: isClicked ? 0 : -UIScreen.main.bounds.width * 0.75, height: 0))

Upvotes: 0

Yrb
Yrb

Reputation: 9675

The problem is the view that gets squished has no explicit width. Understand how views get sized in SwiftUI. The parent view proposes a size, and the child view responds with the size it wants. Before you click the button, the parent view (the HStack) is proposing 100% to the view. That view says that I can do 100%, so no squishing. However, when you make the other view 75% of the parent, the parent can only offer 25% to the first view. The view responds, I can fit if I squish, so it does. See Laying out a simple view.

To fix it, you simply need to explicitly set the width of the first view to 100%, and then it will get pushed over like you were expecting without compression.

struct SlideInView: View {
    @State var isClicked = false
    var body: some View {
        // I swapped out UIScreen.main.bounds for a GeometryReader
        // UIScreen.main.bounds.width always returns the width of the screen
        // even if the parent view does not have that much space to offer, and
        // can lead to some interesting results.
        GeometryReader { geometry in
            HStack{
                Rectangle()
                    .fill(.gray)
                    .ignoresSafeArea()
                    .frame(width: isClicked ? geometry.size.width * 0.75 : 0)
                
                VStack{
                    Text("This gets squished")
                    HStack{
                        Button{
                            withAnimation(.spring()) {
                                isClicked.toggle()
                            }
                        } label: {
                            Image(systemName: "menucard.fill")
                                .padding(.leading)
                        }
                        Spacer()
                    }
                    Spacer()
                }
                .frame(width: geometry.size.width) // Explicitly set width here
            }
        }
    }
}

Upvotes: 2

Related Questions