A configurable framework for presenting bottom sheets on iOS.
- Sheets can be presented in different sizes:
- Full screen sheets are similar to
UIModalPresentationPageSheet
on iOS 13+. - Half screen sheets.
- Fitting size sheets are sized to the content displayed, when smaller than a full screen sheet.
- Full screen sheets are similar to
- Content in the sheet can be embedded in a scroll view to support scrolling when the content height exceeds that of the sheet.
- Supports sliding the sheet between different positions and swiping to dismiss.
- Sheets can be stacked - push additional sheets on and then pop them off.
- Automatically adjusts the sheet when the keyboard appears/disappears.
- Configure display properties like the top inset of a full-screen sheet, optional handle to indicate sheet sliding, dimmed background view and more.
Full Sheet | Half Sheet | Fitting Size Sheet | Fitting Size with a Keyboard |
---|---|---|---|
- iOS 11+
Add .package(url: "https://github.com/livefront/Duvet.git", exact: "0.0.6")
to your Package.swift
file dependencies.
Add pod Duvet
to your Podfile
.
Add github livefront/Duvet
to your Cartfile
.
-
Import
Duvet
into the file that you will present a sheet from.import Duvet
-
Create a
SheetItem
with the view controller that should will be shown in the sheet.let sheetItem = SheetItem( viewController: viewController, configuration: SheetConfiguration(), // See Scroll View Interaction below for an example with a scroll view. scrollView: nil )
-
Create a
SheetViewController
. TheSheetViewController
is a container view controller for a stack of sheets, similar toUINavigationController
. It will allow you to push on and pop off additionalSheetItem
s.let sheetViewController = SheetViewController(sheetItem: sheetItem) sheetViewController.modalPresentationStyle = .custom // Note: `sheetTransitioningDelegate` needs to be retained by the presenting // view controller since `sheetViewController` only keeps a weak reference. let sheetTransitioningDelegate = SheetTransitioningDelegate() sheetViewController.transitioningDelegate = sheetTransitioningDelegate
-
Conform the presenting view controller to
SheetViewControllerDelegate
to handle dismissing the sheet.extension PresentingViewController: SheetViewControllerDelegate { func dismissSheetViewController() { dismiss(animated: true, completion: nil) } }
-
Then the
SheetViewController
can be presented by your presenting view controller.<presentingViewController>.present(sheetViewController, animated: true)
Various parameters can be configured for a sheet via a SheetConfiguration
when creating a SheetItem.
Property | Description |
---|---|
cornerRadius |
The corner radius of the sheet. Defaults to 10. |
dismissKeyboardOnScroll |
True if the keyboard should be dismissed when the sheet view's scroll view is scrolled. Defaults to true . |
handleConfiguration |
An optional configuration for displaying a handle in or above the sheet to indicate that the sheet can be panned. Defaults to nil for no handle. |
initialPosition |
The initial position of the sheet when presented. Defaults to .open for a full sized sheet. |
supportedPositions |
The list of positions that the sheet can be adjusted to via sliding. Defaults to [.open] , which only allows the sheet to be fully sized or closed. |
topInset |
The number of points between the top of the sheet and the top safe area. Defaults to 44. |
isKeyboardAvoidanceEnabled |
A flag that determines if Duvet should use its own custom keyboard avoidance mechanism for this sheet or not. This is defaulted to true , but if your app handles keyboard avoidance in it's own way, it is recommended that this flag is set to false to reduce animation conflicts. |
If the view controller that you want to show in the sheet has a scroll view that wraps the sheet content, Duvet
needs to be able to interact with it so that it knows whether you should be scrolling the scroll view or sliding the sheet.
For this interaction to occur, pass a reference to your view controller's scroll view when creating the SheetItem
:
let sheetItem = SheetItem(
viewController: viewController,
configuration: SheetConfiguration(),
scrollView: viewController.scrollView
)
Duvet
will make the following changes to the scroll view:
Duvet
will become theUIScrollView
delegate. If there was an existingdelegate
on the scroll view,Duvet
will continue forwarding anyUIScrollViewDelegate
methods to the originaldelegate
. It's important to set adelegate
, if necessary, before creating theSheetItem
to continue receivingUIScrollViewDelegate
methods.- Enables
alwaysBounceVertical
. This is required for sliding the sheet or swipe to dismiss. - The sheet view will also add itself as a target of the scroll view's
panGestureRecognizer
.
SheetViewController
supports managing a stack of SheetItem
s. This allows additional sheets to be pushed on and then popped off of the stack.
-
Display an initial sheet.
let sheetItem = SheetItem( viewController: <view controller for the first sheet> configuration: SheetConfiguration(), scrollView: nil ) let sheetViewController = SheetViewController(sheetItem: sheetItem) sheetViewController.delegate = self sheetViewController.modalPresentationStyle = .custom sheetViewController.transitioningDelegate = sheetTransitioningDelegate present(sheetViewController, animated: true)
-
Push an additional sheet onto the sheet stack.
let sheetItem2 = SheetItem( viewController: <view controller for the second sheet> configuration: SheetConfiguration(), scrollView: nil ) sheetViewController.push(sheetItem: sheetItem2, animated: true)
-
Pop that sheet off of the sheet stack.
sheetViewController.pop(animated: true)
There is an example application showing how to present and configure Duvet
for many common use cases in the Example
directory.
This library is released under the Apache 2.0 license. See the LICENSE file for details.