BottomSheet lets you 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
You can easily add awesome animated context menu to your app.

Context-Menu.iOS You can easily add awesome animated context menu to your app. Made in Check this [project on dribbble] (https://dribbble.com/shots/17

Yalantis 1.8k Nov 17, 2022
Panels is a framework to easily add sliding panels to your application

Panels is a framework to easily add sliding panels to your application. It takes care of the safe area in new devices and moving your panel when the k

Antonio Casero 1.5k Dec 14, 2022
Simple side/slide menu control for iOS, no code necessary! Lots of customization. Add it to your project in 5 minutes or less.

▤ SideMenu If you like SideMenu, give it a ★ at the top right of this page. SideMenu needs your help! If you're a skilled iOS developer and want to he

Jon Kent 5.4k Dec 29, 2022
Save all your Mac apps for later with one click 🖱️

Later videoplayback.mp4 Save all your Mac apps for later with one click ??️ Later is a Mac menu bar app that clears and restores your workspace with e

Alyssa X 925 Jan 8, 2023
Custom transition between controllers. Settings controller for your iOS app.

SPLarkController About Transition between controllers to top. You can change animatable height after presentation controller. For presentation and dis

Ivan Vorobei 965 Dec 17, 2022
SwiftySideMenu is a lightweight and easy to use side menu controller to add left menu and center view controllers with scale animation based on Pop framework.

SwiftySideMenu SwiftySideMenu is a lightweight, fully customizable, and easy to use controller to add left menu and center view controllers with scale

Hossam Ghareeb 84 Feb 4, 2022
slider view for choosing categories. add any UIView type as category item view. Fully customisable

CategorySliderView Horizontal or vertical slider view for choosing categories. Add any UIView type as category item view. Fully customisable Demo Inst

Cem Olcay 353 Nov 6, 2022
A custom SwiftUI modifier to present an ActionSheet or a Popover menu

ActionOver A custom SwiftUI modifier to present an Action Sheet on iPhone and a Popover on iPad and Mac. iPhone Preview iPad Preview Mac Preview Featu

Andrea Miotto 127 Dec 21, 2022
Library provides easy to implement variation of Android (Material Design) Floating Action Button for iOS. You can use it as your app small side menu. 🌶

RHSideButtons ?? Library provides easy to implement variation of Android (Material Design) Floating Action Button for iOS. You can use it as your app

Robert Herdzik 166 Nov 14, 2022
This is a spring slide menu for iOS apps - 一个弹性侧滑菜单

LLSlideMenu This is a spring slide menu for iOS apps 一个弹性侧滑菜单 弹性动画原理借鉴该项目中阻尼函数实现 Preview 预览 Installation 安装 pod 1.pod 'LLSlideMenu', '~> 1.0.6'

Li Lei 590 Dec 7, 2022
An implementation of the sliding menu found in various iOS apps.

IIViewDeckController ViewDeck is a framework to manage side menus of all kinds. It supports left and right menus and manages the presentation of the s

ViewDeck 5.3k Dec 20, 2022
RadialMenu is a custom control for providing a touch context menu (like iMessage recording in iOS 8) built with Swift & POP

RadialMenu Looking for help? For $150/hr I'll help with your RadialMenu problems including integrating it into your project. Email [email protected] t

Brad Jasper 297 Nov 27, 2022
Menu controller with expandable item groups, custom position and appearance animation written with Swift. Similar to ActionSheet style of UIAlertController.

Easy to implement controller with expanding menu items. Design is very similar to iOS native ActionSheet presentation style of a UIAlertController. As

Anatoliy Voropay 22 Dec 27, 2022
RadialMenu is a custom control for providing a touch context menu (like iMessage recording in iOS 8) built with Swift & POP

RadialMenu Looking for help? For $150/hr I'll help with your RadialMenu problems including integrating it into your project. Email [email protected] t

Brad Jasper 297 Nov 27, 2022
📷A simple and convenient way to manage your webcam's picture settings, right from your menu bar

Viewfinder A simple and convenient way to manage your webcam's picture settings, right from your menu bar. About • Download • Building from Source • C

Lukas Romsicki 31 Dec 25, 2022
a simple macOS menu bar application that shows you the lyrics of current playing spotify track.

lyricsify a simple macOS menu bar application that shows you the lyrics of current playing spotify track.

Krisna Pranav 4 Sep 16, 2021
An easy to use and setup floating view for your app. 🎡

HHFloatingView An easy to use and setup floating view for your app. ?? Installation Manually - Add HHFloatingView/Source folder to your Project. And y

Hemang 95 Dec 15, 2022
Barber lives in your macOS menu bar and keeps track of what needs to be updated.

Barber Barber is a macOS application to keep track of application updates easily. It lives in your menu bar, and uses homebrew to determine what's out

Max Ainatchi 1 Nov 30, 2021
Control your display's brightness from the macOS menu bar. Simple and easy to use.

MonitorControl Lite Control your display's brightness from the macOS menu bar. Simple and easy to use. About MonitorControl Lite is a simplified versi

null 62 Dec 11, 2022