BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps.

Overview

BottomSheet


Version Platform Swift 5.3 MIT License Twitter: @danielsaidi

About BottomSheet

BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps.

The result can look like this...or completely different:

These sheets have a default style, but can be customized to fit your specific app.

Installation

Swift Package Manager

https://github.com/danielsaidi/BottomSheet.git

CocoaPods

pod DSBottomSheet

How does it work

BottomSheet contains a BottomSheet SwiftUI view, that can be created with an isExpanded binding, a minHeight and maxHeight and a style.

let sheet = BottomSheet(
    isExpanded: $isSheetExpanded,
    minHeight: .points(100)
    maxHeight: .available
    style: .standard
)

Once you have a sheet view, you can add it to any view, using the bottomSheet modifier:

List(items) { item
   HStack { item.name }
}.bottomSheet(sheet)

The sheet will be added above the view and docked to the bottom. The sheet can now be either swiped up or expanded and collapsed by tapping the handle.

Heights

BottomSheet is created with a minHeight and maxHeight, which are BottomSheetHeight enums with these cases:

  • available - the total available height
  • percentage - a percentage of the total available height
  • points - a fixed number of points

You can set these to control how tall your sheet can become and how much it can collapse. You can change these properties at any time.

Styling

BottomSheet is created with a style, which is a BottomSheetStyle with these properties:

  • color - the color of the sheet
  • cornerRadius - the corner radius of the sheet
  • modifier - the modifier to apply to the sheet
  • snapRatio - the percent of the max height, after which the sheet slides to the full height
  • handleStyle - the bottom sheet's handle style

You can define your own bottom sheet and ottom sheet handle styles and apply them when creating a sheet.

Important

This library uses resource-based colors, which aren't available to SwiftUI previews outside of this library.

Make sure to always use the .preview style when previewing a sheet.

Demo app

This repo contains a basic demo app that demonstrates how to use the bottom sheet.

Just open the Demo project and run the app.

Acknowledgements

This library is based on this amazing gist by @mecid. It would not have been made without him, so big thanks for putting that gist out into the world.

Contact me

Feel free to reach out if you have questions or if you want to contribute in any way:

License

BottomSheet is available under the MIT license. See LICENSE file for more info.

Comments
  • Can ScrollViews play nice with content inset?

    Can ScrollViews play nice with content inset?

    I've adapted the demo view with a TabView, and in the bottom sheet a ScrollView instead of List. When using List, the scroll element pushes the bottom of the list upwards to account for the tab selection bar. But when I use a ScrollView, like below, the last item is obscured by the tab selection bar. Do you know how to account for this? Is it a SwiftUI issue?

    image
    
    struct DemoSheetContent: View {
        init(style: BottomSheetStyle) {
            self.style = style
        }
        var style: BottomSheetStyle
        
        var body: some View {
    //        List {
    //            ForEach(1...20, id: \.self) {
    //                Text("\($0)")
    //            }
    //            .listRowBackground(style.color)
    //        }
            ScrollView {
                VStack {
                    ForEach(1...20, id: \.self) { i in
                        HStack {
                            Text("Item \(i)")
                            Spacer()
                            Button(
                                action: { print("Clicked \(i)") },
                                label: {
                                Image(systemName: "tray.and.arrow.down.fill")
                            })
                        }
                        .padding()
                        .frame(minHeight: 50)
                    }
                }
            }
        }
    }
    
    struct DemoSheetContent_Previews: PreviewProvider {
        static var previews: some View {
            DemoSheetContent(style: .standard)
        }
    }
    
    struct ContentView: View {
        var body: some View {
                TabView {
                    SettingsView()
                                .tabItem {
                                    Label("Tab 1", systemImage: "list.dash")
                                }
    
                    SettingsView()
                                .tabItem {
                                    Label("Tab 2", systemImage: "square.and.pencil")
                                }
                }
        }
    }
    
    struct SettingsView: View {
        @State private var useTallerMinHeight = false
        @State private var useShorterMaxHeight = false
        @State private var sheetStyle = BottomSheetStyle.standard
        
        @State private var isExpanded = false
        var body: some View {
            NavigationView {
                List {
                    Section(header: Text("").frame(height: 1)) {
                        Toggle("Is expanded", isOn: $isExpanded)
                        Toggle("Use taller min height", isOn: $useTallerMinHeight)
                        Toggle("Use shorter max height", isOn: $useShorterMaxHeight)
                    }
                    
                    Section(header: Text("Sheet Styles").frame(height: 1)) {
                        sheetStyleButton("Standard", style: .standard)
                        sheetStyleButton("(Demo) Red", style: .demoRed)
                        sheetStyleButton("(Demo) Green", style: .demoGreen)
                        sheetStyleButton("(Demo) Blue", style: .demoBlue)
                        sheetStyleButton("(Demo) Larger Corner Radius", style: .demoRound)
                    }
                    
                    Section(header: Text("Sheet Handle Styles").frame(height: 1), footer: bottomPadding) {
                        sheetHandleStyleButton("Standard", style: .standard)
                        sheetHandleStyleButton("(Demo) Red", style: .demoRed)
                        sheetHandleStyleButton("(Demo) Green", style: .demoGreen)
                        sheetHandleStyleButton("(Demo) Blue", style: .demoBlue)
                        sheetHandleStyleButton("(Demo) Large Yellow", style: .demoLargeYellow)
                    }
                }
                .buttonStyle(PlainButtonStyle())
                .navigationTitle("Bottom Sheet Demo")
                .listStyle(InsetGroupedListStyle())
            }
            .bottomSheet(sheet)
        }
    }
    
    private extension SettingsView {
        
        var bottomPadding: some View {
            Color.clear.frame(height: useTallerMinHeight ? 280 : 130)
        }
        
        var sheet: some BottomSheetView {
            BottomSheet(
                isExpanded: $isExpanded,
                minHeight: useTallerMinHeight ? .points(300) : .points(150),
                maxHeight: useShorterMaxHeight ? .percentage(0.5) : .available,
                style: sheetStyle) {
                DemoSheetContent(style: sheetStyle)
            }
        }
        
        func listButton(_ text: String, action: @escaping () -> Void) -> some View {
            Button(action: action) {
                HStack {
                    Text(text)
                    Spacer()
                }.background(Color.white.opacity(0.0001))
            }
        }
        
        func sheetStyleButton(_ text: String, style: BottomSheetStyle) -> some View {
            listButton(text) {
                sheetStyle = style
            }
        }
        
        func sheetHandleStyleButton(_ text: String, style: BottomSheetHandleStyle) -> some View {
            listButton(text) {
                sheetStyle = BottomSheetStyle(handleStyle: style)
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    
    adjustment 
    opened by digitalheir 6
  • Using BottomSheet with isPresented?

    Using BottomSheet with isPresented?

    Hello 👋🏻

    I'm attempting to use BottomSheet to build my Movie app Sonix (I'm fairly new to Swift/SwiftUI) basically i'm trying to replicate the design which is attached so each movie opens BottomSheet at full width. At the moment the only way I have it working is wrapping inside .sheet using isPresented.

    Any help would be much appreciated

    Screenshot 2021-11-29 at 00 18 25 Screenshot 2021-11-29 at 00 22 43

    feature 
    opened by jakeround 5
  • Allow sheet to be dragged above maxHeight, then snap back to maxHeight on drop

    Allow sheet to be dragged above maxHeight, then snap back to maxHeight on drop

    I think the component would feel a little more playful if we could drag the expanded bottom sheet to above its maxHeight, and on releasing the finger would use the spring animation to move back.

    What do you think?

    Kind of like this, except that it doesn't snap to the 'full screen' height:

    feature wontfix 
    opened by digitalheir 4
  • Apple Music like docking miniview

    Apple Music like docking miniview

    One of the usecases we have for BottomSheet requires us to roll out a mini view and then follow the same interaction like Apple Music i.e:

    • Tap the mini view to reveal the full player
    • Swipe down to dock back into the miniview

    File

    FYI this may also be out of scope for this project.

    PS Anomaly would be willing to sponsor the development of this feature.

    feature 
    opened by devraj 4
  • Does not show in Swift Package Manager

    Does not show in Swift Package Manager

    Am having swift package manager issue. Whenever i try to add it with this link https://github.com/danielsaidi/BottomSheet.git It does not show up in swift package manager.

    Secondly, when i add library manually, it shows could not find module 'Bottom Sheet' for target x86_64-apple-ios-simulator

    when i try to add

    import BottomSheet

    Checked on internet, my device with xcode is with apple silicon m1 imac. Didnt try it on intel mac. But if it is able to add with swift package manager, that is great for me.

    opened by kunalzigma 3
  • Transparent Background on handle?

    Transparent Background on handle?

    Hey 👋🏻

    Just a simple styling issue, i'm looking to make backgroundColor dividerColor both transparent so behind the sheet but I want to keep the handle visible. At full opacity it looks like the properties do work but when set to 0 it looks like there's a base colour behind the sheet any ideas?

    Screenshot 2021-12-29 at 01 08 45

    bug 
    opened by jakeround 3
  • Handle safe area bottom inset differently

    Handle safe area bottom inset differently

    Fix for #2.

    Putting a ScrollView in a bottom sheet caused the system to put the bottom of the content in the safe area, obscuring some content in many cases.

    I guess that maybe specifying the padding(.bottom, geo.safeAreaInsets.bottom) to the content view could be the user's responsibility, because they might intend the view to bleed into the safe area. Maybe it should be an optional 'bleed' setting?

    image
    opened by digitalheir 2
  • About the deprecated notice

    About the deprecated notice

    This library will be deprecated, due to the new SwiftUI 4 custom sheet size capabilities. Since this functionality will be added to SwiftUI, this library is no longer needed.

    In SwiftUI 4, you can use the new presentationDetents view modifier to define custom sheet sizes. You can also make sheets non-dismissable, hide the resize handle, etc.

    I get it, but to my knowledge it isn't possible to interact with the background and hide the scrim behind the sheet. If this is required, is this library still a good fit or am I missing something?

    opened by austincondiff 1
  • On top of tabbar

    On top of tabbar

    Hi @danielsaidi

    Thank you for this fantastic library. I just had a question and wanted to ask here for future reference (and for SEO). How can I make sure that the bottomSheet is shown on top of the Tabbar?

    opened by anonrig 1
  • Drag gesture moves sheet too little

    Drag gesture moves sheet too little

    I noticed now, that when I drag the sheet handle, the sheet moves less than my finger.

    I have tried changing the drag gesture to use .onChange instead of .updating but the result is the same. It's as if the reported translation is incorrect and too small.

    bug wontfix 
    opened by danielsaidi 0
  • Animation crash

    Animation crash

    Schermata 2022-03-15 alle 12 21 39

    Updating .animation(.interactiveSpring()) with its boolean value .animation(.interactiveSpring(), value: isExpanded) the DragGesture of handle breaks the sheet.

    bug 
    opened by gmcusaro 1
Releases(0.3.0)
  • 0.3.0(Jan 5, 2022)

    ✨ New Features

    • BottomSheetStyle and BottomSheetHandleStyle are now mutable.
    • BottomSheetStyle.standard is now mutable and lets you change the standard style.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Nov 29, 2021)

    This release adds macOS support and tweaks the design and behavior of the bottom sheet.

    The handle was previously placed in a non-designable area, under which the sheet content scrolled.

    Now, the area has a divider that separates the handle from the content, with the entire handle bar area being customizable with the handle style.

    💥 New Features

    • The library now compiles for macOS.
    • There is a new BottomSheetHandleBar view.
    • BottomSheetHandleStyle has a new backgroundColor property.
    • BottomSheetHandleStyle has a new dividerColor property.
    • BottomSheetHandleStyle has a new padding property.

    💡 Behavior Changes

    • The sheet handle is now placed in a bar, with padding and a divider.

    🗑 Deprecations

    • BottomSheetHandleStyle color has been renamed to handleColor.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(May 11, 2021)

Owner
Daniel Saidi
Freelance software engineer with focus on mobile products and Apple platforms like iOS, tvOS and watchOS.
Daniel Saidi
Multiplatform (iOS, macOS) SwiftUI bottom sheet drawer. Expandable bottomsheet. Slide out bottom menu

Multiplatform (iOS, macOS) SwiftUI bottom sheet drawer Features It does not re-render the background content while manipulating with the sheet iOS and

Igor 8 Nov 18, 2022
BottomSheet Component 🧪🧪🧪

BCSComponent This Source code provide a bottom sheet which allow you custom own cell,you able to show it on the bottom sheet Get started First thing f

Kien Pham 3 Dec 1, 2022
It is a highly configurable iOS library which allows easy styling with built in styles as well as extra header and footer views so that you can make extremely unique alerts and action sheets.

 CFAlertViewController CFAlertViewController is a library that helps you display and customise Alerts, Action Sheets, and Notifications on iPad and i

Crowdfire Inc. 1.1k Dec 18, 2022
an extension library for SwiftUI sheets.

SheetKit SheetKit is an extension library for SwiftUI sheets. 中文版说明 with Picture What is SheetKit SheetKit is a library of extensions for SwiftUI moda

东坡肘子 65 Dec 31, 2022
SwiftUI native-like onboarding sheets

Welcome Sheet Welcome sheet for swiftUI enables incredibly easy way for creating onboarding screens, update notes, or whatever you imagine! The main i

Jakub Florek 43 Dec 29, 2022
PageSheet - Customizable sheets using UISheetPresentationController in SwiftUI

PageSheet Customizable sheet presentations in SwiftUI. Using UISheetPresentation

Eric Lewis 50 Oct 7, 2022
Share-sheet-example - A sample project that reproduces an issue with Share Sheets

Hello, DTS! This project demonstrates the issue I'm having with the Share Sheet.

Marcos Tanaka 0 Feb 11, 2022
zekunyan 608 Dec 30, 2022
SwiftUI Draggable Bottom Sheet

SwiftUI Draggable Bottom Sheet

paigeshin 2 Mar 3, 2022
Customizable Dynamic Bottom Sheet Library for iOS

DynamicBottomSheet Powerd by Witi Corp., Seoul, South Korea. Fully Customizable Dynamic Bottom Sheet Library for iOS. This library doesn't support sto

Witi Official 10 May 7, 2022
Bottom Sheet component is widely used in Joom application

Bottom Sheet Bottom Sheet component is widely used in Joom application Installation Swift Package Manager Swift Package Manager is a tool for managing

Joom 101 Dec 29, 2022
BottomSheetDemo - Bottom sheet modal view controller with swift

当我们想弹出一个预览视图,bottom sheet modal view controller 非常实用。在 iOS 中,长按拖拽手势可以让 controlle

null 8 Oct 29, 2022
DGBottomSheet - The lightest swift bottom sheet library

DGBottomSheet Requirements Installation Usage DGBottomSheet The lightest swift b

donggyu 9 Aug 6, 2022
Custom-action-sheet- - Custom action sheet with swift

Custom-action-sheet- Usage let alertController: UIAlertControllerDimmed = UIAler

Girisankar G 0 Jan 19, 2022
CoffeeToast - A swift package to easily add Toast notifications to your app

CoffeeToast A simple Swift package to add Toast Notifications to your app. Insta

Maegan Wilson 2 Feb 3, 2022
💬 A tiny extension for UIAlertController that makes working with it very simple. Only 150 lines of code.

AlertController ?? A tiny extension for UIAlertController that makes working with it very simple. Only 150 lines of code. Alert let alert = UIAlertCon

Mezhevikin Alexey 9 Nov 2, 2022
SwiftEntryKit is a presentation library for iOS. It can be used to easily display overlays within your iOS apps.

SwiftEntryKit ?? Donations can be made here. Table of Contents Overview Features Example Project Example Project Installation Presets Playground Requi

Daniel Huri 6.1k Jan 4, 2023
Action sheet allows including your custom views and buttons.

CustomizableActionSheet Action sheet allows including your custom views and buttons. Installation CocoaPods Edit your Podfile: pod 'CustomizableAction

Ryuta Kibe 191 Nov 26, 2021
LCToast - Add toast to UIView.

LCToast Add toast to UIView. Requirements iOS 8.0+ Features The LCToast is a comparison of features with Toast and SVProgressHUD. LCToast Toast SVProg

LiuChang 158 Nov 22, 2022