Card Style Sliding Menu from Bottom

Published by @SoNiceInfo at 5/26/2020


Image of card styl slide menu

Introducing how to create card style slide menu up frome below witch is simillar to the Map and Google App.
Not only my implementation, but also packages published on GitHub.
They are easily used by adding "Swift Pakckages" in Xcode.
I'll create the menu on the map at this time.

Prepare ContentView

First, make a view to display a map.
Detail of creating map in SwiftUI is introduced at Display Map
Call MapView from ContentView. In advance call CardModalView which is crated at next step.

import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack() {
            MapView()
            CardModalView()
        }
    }
}

This card style menu stops at top, middle and bottom.
Override the offsets at onAppear from GeometryReader to fit with screen size.
onChanged in DragGesture allows you to slide while restricting the top and bottom areas to stay in the same area while moving.
onEnded is written in a case-by-case manner so that the card style sliding menu is set to the top, middle, and bottom regions close to each other.

import SwiftUI

struct CardModalView: View {
    @State private var offsets = (top: CGFloat.zero, middle: CGFloat.zero, bottom: CGFloat.zero)
    @State private var offset: CGFloat = .zero
    @State private var lastOffset: CGFloat = .zero
    
    var body: some View {
        GeometryReader { geometry in
            VStack (spacing: 30) {
                RoundedRectangle(cornerRadius: 5)
                    .foregroundColor(.gray)
                    .frame(width: 100, height: 10)
                Text("CardModal")
                    .font(.largeTitle)
                Spacer()
            }
            .padding()
            .frame(width: geometry.size.width, height: geometry.size.height)
            .background(Color.yellow)
            .clipShape(RoundedRectangle(cornerRadius: min(self.offset, 20) ))
            .animation(.interactiveSpring())
            .onAppear {
                self.offsets = (
                    top: .zero,
                    middle: geometry.size.height / 2,
                    bottom: geometry.size.height * 3 / 4
                )
                self.offset = self.offsets.bottom
                self.lastOffset = self.offset
            }
            .offset(y: self.offset)
            .gesture(DragGesture(minimumDistance: 5)
                .onChanged { v in
                    let newOffset = self.lastOffset + v.translation.height
                    if (newOffset > self.offsets.top && newOffset < self.offsets.bottom) {
                        self.offset = newOffset
                    }
                }
                .onEnded{ v in
                    if (self.lastOffset == self.offsets.top && v.translation.height > 0) {
                        if (v.translation.height < geometry.size.height / 2) {
                            self.offset = self.offsets.middle
                        } else {
                            self.offset = self.offsets.bottom
                        }
                    } else if (self.lastOffset == self.offsets.middle) {
                        if (v.translation.height < 0) {
                            self.offset = self.offsets.top
                        } else {
                            self.offset = self.offsets.bottom
                        }
                    } else if (self.lastOffset == self.offsets.bottom && v.translation.height < 0) {
                        if (abs(v.translation.height) > geometry.size.height / 2) {
                            self.offset = self.offsets.top
                        } else {
                            self.offset = self.offsets.middle
                        }
                    }
                    self.lastOffset = self.offset
                }
            )
        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct CardModalView_Previews: PreviewProvider {
    static var previews: some View {
        CardModalView()
    }
}

Use Swift Packages

Swift Packages bring various functions to your app.https://github.com/moifort/swiftUI-slide-over-card are recommended. In Xcode, Select [Project] [PROJECT_NAME] [Swift Packages] then add URL above with [+] button. Here is a usage of the package.

import SwiftUI
import SlideOverCard

struct ContentView: View {
    @State private var position = CardPosition.bottom
    @State private var background = BackgroundStyle.solid

    var body: some View {
        ZStack() {
            MapView()
            SlideOverCard($position, backgroundStyle: $background) {
                VStack {
                    Text("Slide Over Card").font(.title)
                    Spacer()
                }
            }
        }
    }
}

References

moifort/swiftUI-slide-over-card: Slide over modal/card for SwiftUI

P.S.

How to create sliding menu comming from top, left and right.
Sliding Menu from Top, Left and Right



    I released iOS App!

    ToDo App

    Visualize Activity, Data sharing with iCloud, Dark mode supported.

    リリースしたToDoアプリのスクリーンショット

    IP Address bookmark.

    Check and bookamrk IP address of all interfaces with geolocation.

    リリースしたIPアドレス保存アプリのスクリーンショット