Swipable fullscreen modal like Apple music lyrics

I would like to have a fullscreen "view" that slides up from the bottom after a user action, and that can be swiped down smoothly like a "sheet".

I understand that there are sheets that are swipable, but dont really cover the fullscreen, and then that there is fullscreencover which does cover the full screen but is not swipable.

In apple music, whenever you click on a song, a fullscreen "modal" slides up from the bottom, and is swipable. How can I achieve that. I'm guessing if apple does it on their apps, they allow users to have the possibility to achieve the same results.

Post not yet marked as solved Up vote post of aurelien_ Down vote post of aurelien_
1.8k views

Replies

You can use the fullScreenCover modifier and add a gesture to it to swipe it down and make it disappear as shown in the full example below:

struct FullScreenPanelSimulation: View {
    @State var showNewScreen = false
    @State private var verticalPosition = 0.0
    
    var body: some View {
        VStack {
            button
        }
        .fullScreenCover(isPresented: $showNewScreen) {
            fullScreenPanel
        }
    }
    
    var button: some View {
        Button {
            showNewScreen.toggle()
        } label: {
            Text("Show full screen")
                .padding(20)
                .foregroundColor(.white)
                .background(.purple)
                .cornerRadius(8)
        }
    }
    
    var fullScreenPanel: some View {
        ZStack(alignment: .top) {
            RoundedRectangle(cornerRadius: 20)
                .fill(Gradient(colors: [.purple.opacity(0.9), .purple.opacity(0.1)]))
                .ignoresSafeArea(.all)
            VStack {
                Spacer()
                Text("Slide me down to make me disappear...")
                    .font(.largeTitle)
                Spacer()
            }
            
        }
        .offset(y: verticalPosition)
        .gesture(
            gestureVertical()
        )
        .transition(.slide)
    }
    
    func gestureVertical() -> some Gesture {
        return DragGesture()
            .onChanged { value in
                if value.translation.height < 0 {
                    verticalPosition = 0
                } else {
                    verticalPosition = value.translation.height
                }
                
            }
            .onEnded { value in
                withAnimation(.linear(duration: 0.05)) {
                    showNewScreen.toggle()
                    verticalPosition = .zero
                }
            }
    }
}