Home > Back-end >  How to drag to expand for safe area content?
How to drag to expand for safe area content?

Time:01-06

I have a content in the safe area that reveals details on a button tap. I would like to allow the user to drag the handle to expand the content details as well, almost like the bottom sheet behaviour.

This is what I have so far:

struct ContentView: View {
    @State private var isExpanded = false

    var body: some View {
        ScrollView {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
            }
            .frame(maxWidth: .infinity)
            .padding()
        }
        .safeAreaInset(edge: .bottom) {
            VStack(spacing: 24) {
                Capsule()
                    .frame(width: 50, height: 5)
                    .opacity(0.5)
                HStack(spacing: 16) {
                    Image(systemName: "list.bullet")
                    Spacer()
                    VStack {
                        Text("Track 2")
                    }
                    .font(.caption)
                    Spacer()
                    Image(systemName: "backward.fill")
                    Image(systemName: "play.fill")
                    Image(systemName: "forward.fill")
                    Spacer()
                    Button {
                        withAnimation {
                            isExpanded.toggle()
                        }
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                }
                if isExpanded {
                    HStack {
                        Image(systemName: "airplayaudio")
                        Image(systemName: "timer")
                        Spacer()
                        Text("Artist")
                    }
                }
            }
            .padding([.bottom, .horizontal])
            .padding(.top, 8)
            .background(.ultraThinMaterial)
        }
    }
}

enter image description here

How can I add functionality to the Capsule to drag the handle to expand that animates in a way to stretch the content in an elastic way (not just swipe to expand, but to drag the content until the user let's go)?

CodePudding user response:

You can use a DragGesture on your Capsule:

.gesture(
    DragGesture()
        .onEnded { value in
            withAnimation(.spring()) {
                let swipingUp = value.translation.height < 0
                withAnimation {
                    isExpanded = swipingUp ? true: false
                }
            }
        }
)

To allow for a stretch, you can use onChanged paired with a State variable:

struct ContentView: View {
    @State private var isExpanded = false
    @State private var stretch = CGFloat.zero
    var body: some View {
        ScrollView {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
            }
            .frame(maxWidth: .infinity)
            .padding()
        }
        .safeAreaInset(edge: .bottom) {
            VStack(spacing: 24) {
                Capsule()
                    .frame(width: 50, height: 5)
                    .opacity(0.5)
                    .gesture(
                        DragGesture()
                            .onChanged { newValue in
                                withAnimation(.spring()) {
                                    stretch = abs(newValue.translation.height)
                                }
                            }.onEnded { value in
                                withAnimation(.spring()) {
                                    isExpanded = value.translation.height < 0
                                    stretch = .zero
                                }
                            }
                    )
                HStack(spacing: 16) {
                    Image(systemName: "list.bullet")
                    Spacer()
                    VStack {
                        Text("Track 2")
                    }
                    .font(.caption)
                    Spacer()
                    Image(systemName: "backward.fill")
                    Image(systemName: "play.fill")
                    Image(systemName: "forward.fill")
                    Spacer()
                    Button {
                        withAnimation {
                            isExpanded.toggle()
                        }
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                }
                if isExpanded {
                    HStack {
                        Image(systemName: "airplayaudio")
                        Image(systemName: "timer")
                        Spacer()
                        Text("Artist")
                    }
                }
                if stretch > 0 && !isExpanded {
                    Color.clear
                        .frame(height: stretch)
                }
            }.padding([.bottom, .horizontal])
            .padding(.top, 8)
            .background(.ultraThinMaterial)
        }
    }
}

  • Related