SkillAgentSearch skills...

BottomSheet

A sliding Sheet from the bottom of the Screen with 3 States build with SwiftUI.

Install / Use

/learn @lucaszischka/BottomSheet
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

BottomSheet

SwiftPM compatible GitHub version CocoaPods compatible CocoaPods version License Issues

A sliding sheet from the bottom of the screen with custom states build with SwiftUI.

Version 3 is out now!

Please look here and read the README for more information on the changes.

Why

There have been many different attempts to recreate the BottomSheet from Apple Maps, Shortcuts and Apple Music, because Apple unfortunately does not provide it in their SDK. (Update: It was more or less added in iOS 16)

However, most previous attempts share a common problem: The height does not change in the different states. Thus, the BottomSheet is always the same size (e.g. 800px) and thus remains 800px, even if you only see e.g. 400px - the rest is inaccessible unless you pull the BottomSheet up to the very top.

There are also many implementations out there that only have 2 states - not 3 like e.g. Apple Maps.

Features

  • Very easy to use
  • Build in header/title (see Parameters)
  • Many view modifiers for customisation (see Modifiers)
  • Fully customisable States (any number of states at any height) (see BottomSheetPosition)
  • States can have the height of their content, absolute pixel values or percentages of the screen height (see BottomSheetPosition)
  • Support for SearchBar in the header
  • It works with ScrollView, List and every other view
  • Can hide the content when in ...Bottom position like Apple does
  • Imbedded .appleScrollBehaviour() modifier, to replicate Apple's ScrollView behaviour
  • Completely animated
  • And much more...

Requirements

  • iOS 13, macCatalyst 13, macOS 10.15
  • Swift 5.5
  • Xcode 12

Installation

Swift Package Manager

The preferred way of installing BottomSheet is via the Swift Package Manager.

Xcode 11 integrates with libSwiftPM to provide support for iOS, watchOS, and tvOS platforms.

  1. In Xcode, open your project and navigate to FileAdd Packages
  2. Paste the repository URL (https://github.com/lucaszischka/BottomSheet) and click Next.
  3. For Rules, select Up to Next Major Version.
  4. Click Add Package.

CocoaPods

BottomSheet is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'BottomSheetSwiftUI'

Now run pod install in the Terminal to install this dependency.

Usage

WARNING: This is Sample Code for visualisation where and how to use, without a working initializer. Please see Examples for working code.

BottomSheet is similar to the built-in Sheet:

struct ContentView: View {

    @State var bottomSheetPosition: BottomSheetPosition = .middle //1
    
    var body: some View {
    
        Map() //2
            .bottomSheet() //3
    }
}

//1 The current State of the BottomSheet.

  • For more information about the possible positions see BottomSheetPosition.
  • If you don't want the BottomSheet to be drag-able and the state to be switchable, you can use the .isResizable(false) modifier.

//2 The view which the BottomSheet overlays.

  • Important: If you want to overlay a TabBar or a NavigationView, you need to add the BottomSheet on a higher level.

//3 This is how you add the BottomSheet - easy right?

Parameters

Title as Header Content

.bottomSheet(
    bottomSheetPosition: Binding<BottomSheetPosition>,
    switchablePositions: [BottomSheetPosition],
    title: String?,
    content: () -> MContent
)

bottomSheetPosition: A binding that holds the current position/state of the BottomSheet.

  • If you don't want the BottomSheet to be drag-able and the state to be switchable, you can use the .isResizable(false) modifier.
  • For more information about the possible positions see BottomSheetPosition.

switchablePositions: An array that contains the positions/states of the BottomSheet.

  • Only the positions/states contained in the array can be switched into (via tapping the drag indicator or swiping the BottomSheet).
  • For more information about the possible positions see BottomSheetPosition.

title: A String that is displayed as title.

  • You can use a view that is used as header content instead.

content: A view that is used as main content for the BottomSheet.

Custom Header Content

.bottomSheet(
    bottomSheetPosition: Binding<BottomSheetPosition>,
    switchablePositions: [BottomSheetPosition],
    headerContent: () -> HContent?,
    mainContent: () -> MContent
)

bottomSheetPosition: A binding that holds the current position/state of the BottomSheet.

  • If you don't want the BottomSheet to be drag-able and the state to be switchable, you can use the .isResizable(false) modifier.
  • For more information about the possible positions see BottomSheetPosition.

switchablePositions: An array that contains the positions/states of the BottomSheet.

  • Only the positions/states contained in the array can be switched into (via tapping the drag indicator or swiping the BottomSheet).
  • For more information about the possible positions see BottomSheetPosition.

headerContent: A view that is used as header content for the BottomSheet.

  • You can use a String that is displayed as title instead.

mainContent: A view that is used as main content for the BottomSheet.

Modifiers

The ViewModifiers are used to customise the look and feel of the BottomSheet.

.enableAccountingForKeyboardHeight(Bool): Adds padding to the bottom of the main content when the keyboard appears so all of the main content is visible.

  • If the height of the sheet is smaller than the height of the keyboard, this modifier will not make the content visible.
  • This modifier is not available on Mac, because it would not make sense there.

.enableAppleScrollBehavior(Bool): Packs the mainContent into a ScrollView.

  • Behaviour on the iPhone:
    • The ScrollView is only enabled (scrollable) when the BottomSheet is in a ...Top position.
    • If the offset of the ScrollView becomes less than or equal to 0, the BottomSheet is pulled down instead of scrolling.
    • In every other position the BottomSheet will be dragged instead
  • This behaviour is not active on Mac and iPad, because it would not make sense there.
  • Please note, that this feature has sometimes weird flickering, when the content of the ScrollView is smaller than itself. If you have experience with UIKit and UIScrollViews, you are welcome to open a pull request to fix this.

.enableBackgroundBlur(Bool): Adds a fullscreen blur layer below the BottomSheet.

  • The opacity of the layer is proportional to the height of the BottomSheet.
  • The material can be changed using the .backgroundBlurMaterial() modifier.

.backgroundBlurMaterial(VisualEffect): Changes the material of the blur layer.

  • Changing the material does not affect whether the blur layer is shown.
  • To toggle the blur layer please use the .enableBackgroundBlur() modifier.

.showCloseButton(Bool): Adds a close button to the headerContent on the trailing side.

  • To perform a custom action when the BottomSheet is closed (not only via the close button), please use the .onDismiss() option.

.enableContentDrag(Bool): Makes it possible to resize the BottomSheet by dragging the mainContent.

  • Due to imitations in the SwiftUI framework, this option has no effect or even makes the BottomSheet glitch if the mainContent is packed into a ScrollView or a List.

.customAnimation(Animation?): Applies the given animation to the BottomSheet when any value changes.

.customBackground(...): Changes the background of the BottomSheet.

  • This works exactly like the native SwiftUI .background(...) modifier.
  • Using offset or shadow may break the hiding transition.

.onDragChanged((DragGesture.Value) -> Void): Adds an action to perform when the gesture’s value changes.

.onDragEnded((DragGesture.Value)): Adds an action to perform when the gesture ends.

.dragPositionSwitchAction((GeometryProxy, DragGesture.Value) -> Void): Replaces the action that will be performed when the user drags the sheet down.

  • The GeometryProxy and DragGesture.Value parameter can be used for calculations.
  • You need to switch the positions, account for the reversed drag direction on iPad and Mac and dismiss the keyboard yourself.
  • Also the swipeToDismiss and flickThrough features are triggered via this method. By replacing it, you will need to handle both yourself.
  • The GeometryProxy's height contains the bottom safe area inserts on iPhone.
  • The GeometryProxy's height contains the top safe area inserts on iPad and Mac.

.showDragIndicator(Bool): Adds a drag indicator to the BottomSheet.

  • On iPhone it is centered above the headerContent.
  • On Mac and iPad it is centered above the mainContent,
  • To change the color of the drag indicator please use the .dragIndicatorColor() modifier.

.dragIndicatorColor(Color): Changes the color of the drag indicator.

  • Chang
View on GitHub
GitHub Stars1.2k
CategoryDevelopment
Updated5d ago
Forks163

Languages

Swift

Security Score

100/100

Audited on Mar 26, 2026

No findings