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

Overview

SwiftUI Drawer

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

Contents

Package

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]) {
        Color.blue
    }.edgesIgnoringSafeArea(.vertical)
}

See the full Reference Guide.

Examples

A multi-height drawer with haptic impact.

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

See more Examples

🚀 Looking for more fun SwiftUI Packages?

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

Credits

Comments
  • Drawer doesn't work in 0.1.0

    Drawer doesn't work in 0.1.0

    Issue

    The title might be a bit ambiguous, but using version 0.1.0 I just can not get the drawer to work or appear at all. Downgrading to 0.0.3 makes all my drawers work again. Not sure whether I missed something obvious, but this can be easily reproduced:

    Steps to reproduce

    1. Create new SwiftUI project
    2. Add swiftui-drawer as package dependency
    3. Add import Drawer to ContentView.swift
    4. Add Drawer of choice to a ZStack in Body
    Screen Shot 2020-09-03 at 16 48 01
    opened by thealpa 7
  • Programmatically set the current height & disable gestures

    Programmatically set the current height & disable gestures

    Feature Request

    I'd like to be able to programmatically set the height of the drawer. I'd also like to be able to set whether or not the drawer can be moved.. without disabling everything within the drawer.

    Example use case:

    A searchBar textField is tapped, then the drawer is set to the mid or high resting point (Animated). The drawer is not able to be moved while the user is searching for something.

    Potential Solution:

    This can be done with a binding variable "currentHeight" or possibly a closure? Maybe have a isDrawerMoveable variable? To disable gestures.

    opened by JamesSedlacek 2
  • Drawer offsets main view content

    Drawer offsets main view content

    This is probably something I'm doing wrong rather than an issue but whatever height I set the drawer to, the content behind it is offset by that amount. ZStack(alignment: .top){ SomeView() Drawer(heights: [100, 200]) { Color.blue } }.edgesIgnoringSafeArea(.vertical)

    SomeView() will then be offset by 200

    opened by esmondmissen 1
  • Give a property to set the most top view

    Give a property to set the most top view

    How to make drawer can display on tabbar. If use ZStack in tabbar. It seems to make tabbar's codes be heavy. And we have to use @environmentObject to handle the status(isPresent). How about your idea?

    opened by ericji-o 0
  • Disable Gesture Dismissal

    Disable Gesture Dismissal

    Use Case Allows dismissal via a physical click of a cancel/save button

    Code Implementation

    Following SwiftUI declarative syntax, the method header (based on syntax of View.disabled):

    public func locked(_ locked: Bool) -> Drawer<Content>
    

    And the ideal use of the implementation could look like:

    struct ContentView: View {
        
        @State var drawerLocked: Bool
    
        var body: some View {
            Drawer(heights: [100, 340]) {
                Color.blue
            }.locked(drawerLocked)
        }
    }
    

    UI Implementation When locked, the view should not change its resting height when dragged.

    The trivial solution is to disable the drag altogether. A more elegant design I propose is for the drawer to react as a stiff spring that only springs up and down a few pixels when attempted to drag. This already happens when you attempt to pull the drawer past the maximum height (see Drawer.springHeight).

    Reasoning: Disabling the gesture altogether might seem unresponsive and could possibly frustrate a user. Using a hard-spring reaction will better convey interactivity that is purposely disabled.

    Suggested by u/pupdogg007

    opened by maustinstar 0
  • Cannot define dynamic height for my drawer

    Cannot define dynamic height for my drawer

    I want my drawer heights (resting points) to be, [100, vStackHeight], so instead of giving it a constant number, i want the second resting height to be the height of the VStack, but I couldn't achieve it even when wrapping the VStack with GeometryReader, since the GeometryReader takes the the size of its parent (the ZStack), any idea how to achieve this behaviour?

        @State var drawerHeights: [CGFloat] = [100, 200]
    
        var body: some View {
            Drawer(heights: $drawerHeights) {
                ZStack(alignment: .top) {
                    RoundedRectangle(cornerRadius: 24)
                        .foregroundColor(.white100)
                        .shadow(radius: 10)
                    
                    VStack(spacing: 0) {
                        Spacer().frame(height: 40)
                            .background(Color.yellow)
                        Text("How it works")
                            .textStyle(.headlinePrimaryCenter)
                        Spacer().frame(height: 40)
                        Text("How it works")
                            .textStyle(.headlinePrimaryCenter)
                        Text("How it works")
                            .textStyle(.headlinePrimaryCenter)
                        Text("How it works")
                            .textStyle(.headlinePrimaryCenter)
                        Text("How it works")
                            .textStyle(.headlinePrimaryCenter)
                        Text("How it works3")
                            .textStyle(.headlinePrimaryCenter)
                        Text("How it works4")
                            .textStyle(.headlinePrimaryCenter)
                    }
                }
            }
            .edgesIgnoringSafeArea(.vertical)
        }
    

    Basically I want the drawer second height (rest) to be dynamic and computed based on its children's height.

    opened by oronbz 1
  • Keyboard hides drawer

    Keyboard hides drawer

    Hello, is there a way to change the position of the drawer when the keyboard appears ? I have a search bar on the drawer and when I click in it, the keyboard covers the drawer.

    This is my Code: I would like to use the ".onTapGesture" to bring the drawer to the top position... image

    This is how it looks now. image

    Sorry, I'm still a noob with Swift and I am just learning it now. 🤓

    opened by LuegM 0
  • Cannot import:

    Cannot import: "No such Module 'Drawer'"

    I've added Drawer via SPM in Xcode 13 beta. I can't import the library:

    image

    From the syntax highlighting it seems that Xcode does import the library anyway, because it's fine with this:

    Drawer(heights: [100, 340]) { ... }
    

    I've already cleaned, quit Xcode, deleted derived data, etc.

    bug 
    opened by LinusGeffarth 2
  • Scroll Gesture Distinction

    Scroll Gesture Distinction

    Problem When a ScrollView is embedded in the drawer, drags within the scroll view are used as scrolls and do not move the drawer. The current workaround is to have a larger "grabable" area at the top of the drawer.

    Ideal Solution When using the maps app, there is a velocity threshold. Drags below the threshold are interpreted as scrolls, while faster drags are used to move the drawer.

    Suggested by u/ms4324

    opened by maustinstar 6
Releases(v0.1.0)
  • v0.1.0(Aug 21, 2020)

    Landscape!

    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.

    Source code(tar.gz)
    Source code(zip)
  • v0.0.4-beta(Jul 18, 2020)

    [Beta] Landscape

    Introducing behaviors for landscape and split view orientations

    New View Modifiers

    Alignment

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

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

    Usage

    Drawer(heights: [100, 340]) {
        Color.blue
    }
    .width(.constant(340))
    .alignment($alignment)
    

    Width

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

    Usage

    Drawer(heights: [100, 340]) {
        Color.blue
    }.width(.constant(340))
    

    OnLayoutForSizeClass

    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]) {
        Color.blue
    }
    .onLayoutForSizeClass { (sizeClass) in
        switch (sizeClass.horizontal, sizeClass.vertical) {
        case (.compact, .compact):
            // smaller iPhone landscape
            break
        case (.compact, .regular):
            // iPhone portrait
            // iPad portrait splitview
            // iPad landscape smaller splitview
            break
        case (.regular, .compact):
            // larger iPhone landscape
            break
        case (.regular, .regular):
            // iPad fullscreen
            // iPad landscape larger splitview
            break
        default:
            // Unknown layout
            break
        }
    }
    
    Source code(tar.gz)
    Source code(zip)
  • v0.0.3(Jul 16, 2020)

    🥳 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.

    /*Drawer*/.spring(20)
    

    😴 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
        print(restingHeight)
    }
    

    💥 Impact

    Sets the haptic impact of the drawer when resting

    /*Drawer*/.impact(.light)
    

    Other Notes

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

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Jul 14, 2020)

    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.

    Source code(tar.gz)
    Source code(zip)
Owner
Michael Verges
CS ’21 student @ Georgia Tech
Michael Verges
Pull up controller with multiple sticky points like in iOS Maps

PullUpController Create your own pull up controller with multiple sticky points like in iOS Maps Features Multiple sticky points Landscape support Scr

Mario Iannotta 1.2k Dec 22, 2022
SwiftUI-Drawer - A bottom-up drawer in swiftUI

SwiftUI-Drawer A bottom-up drawer view. Contents Installation Examples Installat

Bruno Wide 9 Dec 29, 2022
A controller that uses a UIStackView and view controller composition to display content in a list

StackViewController Overview StackViewController is a Swift framework that simplifies the process of building forms and other static content using UIS

Seed 867 Dec 27, 2022
UI Component. This is a copy swipe-panel from app: Apple Maps, Stocks. Swift version

ContainerController UI Component. This is a copy swipe-panel from app: https://www.apple.com/ios/maps/ Preview Requirements Installation CocoaPods Swi

Rustam 419 Dec 12, 2022
DrawerKit lets an UIViewController modally present another UIViewController in a manner similar to the way Apple's Maps app works.

DrawerKit What is DrawerKit? DrawerKit is a custom view controller presentation mimicking the kind of behaviour in the Apple Maps app. It lets any vie

Babylon Health 773 Dec 27, 2022
⬆️ A SwiftUI view component sliding in from bottom

⬆️ A SwiftUI view component sliding in from bottom

Tieda 595 Dec 28, 2022
SwiftUI-Drawer A bottom-up drawer view.

SwiftUI-Drawer A bottom-up drawer view.

Perimeter AI Inc. 9 Dec 29, 2022
A library to imitate the iOS 10 Maps UI.

Pulley A library to imitate the drawer in Maps for iOS 10/11. The master branch follows the latest currently released version of Swift. If you need an

52inc 2k Dec 29, 2022
Anchorage - Single file UIView drag and drop system

anchorage Single file UIView drag and drop system anchors.mp4 License Copyright

● ●●   ● 3 Jan 28, 2022
BulletinBoard is an iOS library that generates and manages contextual cards displayed at the bottom of the screen

BulletinBoard is an iOS library that generates and manages contextual cards displayed at the bottom of the screen. It is especially well

Alexis (Aubry) Akers 5.3k Jan 2, 2023
List tree data souce to display hierachical data structures in lists-like way. It's UI agnostic, just like view-model and doesn't depend on UI framework

SwiftListTreeDataSource List tree data souce to display hierachical data structures in lists-like way. It's UI agnostic, just like view-model, so can

Dzmitry Antonenka 26 Nov 26, 2022
A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI.

A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI. Many of these helpers are useful even in a pure UIKit project.

SwiftUI+ 6 Oct 28, 2022
iOS custom controller used in Jobandtalent app to present new view controllers as cards

CardStackController iOS custom controller used in the Jobandtalent app to present new view controllers as cards. This controller behaves very similar

jobandtalent 527 Dec 15, 2022
AGCircularPicker is helpful component for creating a controller aimed to manage any calculated parameter

We are pleased to offer you our new free lightweight plugin named AGCircularPicker. AGCircularPicker is helpful for creating a controller aimed to man

Agilie Team 617 Dec 19, 2022
A child view controller framework that makes setting up your parent controllers as easy as pie.

Description Family is a child view controller framework that makes setting up your parent controllers as easy as pie. With a simple yet powerful publi

Christoffer Winterkvist 246 Dec 28, 2022
A library, which adds the ability to hide navigation bar when view controller is pushed via hidesNavigationBarWhenPushed flag

HidesNavigationBarWhenPushed A library, which adds the ability to hide navigation bar when view controller is pushed via hidesNavigationBarWhenPushed

Danil Gontovnik 55 Oct 19, 2022
Highly customizable Action Sheet Controller with Assets Preview written in Swift

PPAssetsActionController Play with me ▶️ ?? If you want to play with me, just tap here and enjoy! ?? ?? Show me ?? Try me ?? The easiest way to try me

Pavel Pantus 72 Feb 4, 2022
Reel Search is a Swift UI controller that allows you to choose options from a list

REEL SEARCH Reel Search is a Swift UI controller that allows you to choose options from a list We specialize in the designing and coding of custom UI

Ramotion 2.5k Dec 21, 2022
Infinite paging controller, scrolling through contents and title bar scrolls with a delay

PageController PageController is infinite paging controller, scrolling through contents and title bar scrolls with a delay. Then it provide user inter

Hirohisa Kawasaki 408 Nov 28, 2022