A configurable framework for presenting bottom sheets on iOS.
- Sheets can be presented in different sizes:
- Full screen sheets are similar to
UIModalPresentationPageSheeton 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.7") to your Package.swift file dependencies.
Add pod Duvet to your Podfile.
Add github livefront/Duvet to your Cartfile.
-
Import
Duvetinto the file that you will present a sheet from.import Duvet -
Create a
SheetItemwith 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. TheSheetViewControlleris a container view controller for a stack of sheets, similar toUINavigationController. It will allow you to push on and pop off additionalSheetItems.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
SheetViewControllerDelegateto handle dismissing the sheet.extension PresentingViewController: SheetViewControllerDelegate { func dismissSheetViewController() { dismiss(animated: true, completion: nil) } }
-
Then the
SheetViewControllercan 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:
Duvetwill become theUIScrollViewdelegate. If there was an existingdelegateon the scroll view,Duvetwill continue forwarding anyUIScrollViewDelegatemethods to the originaldelegate. It's important to set adelegate, if necessary, before creating theSheetItemto continue receivingUIScrollViewDelegatemethods.- 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 SheetItems. 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.