Swiftpack.co - Package - maustinstar/swiftui-drawer

SwiftUI Drawer

A SwiftUI bottom-up controller, like in the Maps app. Drag to expand or minimize.



For Xcode Projects

File > Swift Packages > Add Package Dependency: https://github.com/maustinstar/swiftui-drawer

For Swift Packages

Add a dependency in your your Package.swift

.package(url: "https://github.com/maustinstar/swiftui-drawer.git", from: "0.1.0"),

Basic Usage

Embed your view content in a ZStack with the Drawer as the last element. The heights parameter defines a list of resting heights for the drawer.

ZStack {

    ScrollView {
    Drawer(heights: [100, 340]) {

See the full Reference Guide.


A multi-height drawer with haptic impact.

Drawer {
    ZStack {
        RoundedRectangle(cornerRadius: 12)
            .shadow(radius: 100)
        VStack(alignment: .center) {
            Spacer().frame(height: 4.0)
            RoundedRectangle(cornerRadius: 3.0)
                .frame(width: 30.0, height: 6.0)
.rest(at: .constant([100, 340, UIScreen.main.bounds.height - 40]))

See more Examples

🚀 Looking for more fun SwiftUI Packages?

Take your SwiftUI apps to the next level with these Packages!



Stars: 168


Used By

Total: 0


Landscape - 2020-08-21 16:16:48


Check out the new .onLayoutForSizeClass modifier!

With .onLayoutForSizeClass you can update your state variables to layout your drawer for landscape.

Resting heights are now binding!

This means you can keep a state variable to contain your view heights. Instead of using the old .locked modifier, just change the resting heights parameter to a single height.

New Documentation Layout

The Readme is more concise, but it now contains links to other markdown files for Examples and Reference.

[Beta] Landscape - 2020-07-18 21:17:48

[Beta] Landscape

Introducing behaviors for landscape and split view orientations

New View Modifiers


Defines the horizontal alignment for the drawer. The default is fullscreen.

public enum DrawerAlignment {
    case leading, center, trailing, fullscreen


Drawer(heights: [100, 340]) {


Defines a width for the drawer when not in fullscreen alignment.


Drawer(heights: [100, 340]) {


A callback to receive updates when the drawer is laid out for a new size class.

This closure is executed every time the device layout changes (portrait, landscape, and split view). Use this to modify your view when the drawer's layout changes.

Usage Alter the resting heights and alignment when the screen layout changes.

Drawer(heights: [100, 340]) {
.onLayoutForSizeClass { (sizeClass) in
    switch (sizeClass.horizontal, sizeClass.vertical) {
    case (.compact, .compact):
        // smaller iPhone landscape
    case (.compact, .regular):
        // iPhone portrait
        // iPad portrait splitview
        // iPad landscape smaller splitview
    case (.regular, .compact):
        // larger iPhone landscape
    case (.regular, .regular):
        // iPad fullscreen
        // iPad landscape larger splitview
        // Unknown layout

Declarative View Modifiers - 2020-07-16 17:55:05

🥳 New Declarative View Modifiers!

Thank you for all your support and feature requests!

New declarative view modifiers will help you customize the drawer experience. Shoutout to u/pupdogg007 for my favorite requested feature: locking the drawer.

I also spent a lot of time refining the fluidity of the drawer animations. In v0.0.2, I made the drags more responsive by toggling animation. In v0.03, after formulating a function on Desmos, I am introducing 'springiness' to the drawers when they are pulled beyond their boundaries. This animation feels more fluid by asymptotically reducing the influence of a drag.

🔒 Locked

Locks the drawer in a controlled position. When set to true, the drawer will animate into the locked height.

Lock into the current resting height

/*Drawer*/.locked($locked) { (currentPosition) in
    return currentPosition

🪀 Spring

Sets the springiness of the drawer when pulled past boundaries.

The user's drag displacement is transformed by a logistic curve for a natural hard-spring pull that reaches an asymptote.


😴 OnRest

A callback to receive updates when the drawer reaches a new resting level. This closure is executed every time the drawer reaches a new resting hieght. Use this when you want to receive updates on the drawer's changes.

/*Drawer*/.onRest { (restingHeight) in

💥 Impact

Sets the haptic impact of the drawer when resting


Other Notes

Deprecated the init's impact parameter to prefer the declarative modifier.

Animation & Haptics - 2020-07-14 22:38:26

Edited Animation

The animation is now disabled when dragging for increased responsiveness, and the release animation is now a spring.

New Haptics

Haptic impact can be defined when the drawer reaches a resting height, using a UIImpactFeedbackGenerator.

Initial Release - 2020-07-14 20:50:07