Presenting and Customizing Sheets
Sheets in UIKit, introduced in iOS 15, provide a modern, flexible way to present modal content that slides up from the bottom of the screen, often used for forms, options, or contextual actions. Sheets are implemented using UISheetPresentationController
, which offers customizable presentation styles and behaviors. This document covers presenting sheets and customizing their appearance and behavior in UIKit.
Presenting Sheets
Sheets are presented using a UIViewController
configured with a UISheetPresentationController
. This controller manages the sheet’s presentation, allowing it to appear as a partial or full-screen modal view.
Key Components
- UISheetPresentationController: Manages the sheet’s presentation, size, and interaction.
- UIViewController: The view controller presented as a sheet.
- Detents: Define the sheet’s height (e.g.,
.medium
,.large
, or custom). - Presentation Style: Set to
.formSheet
or.pageSheet
for sheet behavior.
Basic Example
Present a view controller as a sheet with medium and large detents:
import UIKit
class ViewController: UIViewController {
@IBAction func presentSheet(_ sender: UIButton) {
let sheetViewController = SheetViewController()
sheetViewController.modalPresentationStyle = .pageSheet
if let sheet = sheetViewController.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
}
present(sheetViewController, animated: true, completion: nil)
}
}
class SheetViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let label = UILabel()
label.text = "Sheet Content"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
Key Points
- Modal Presentation Style: Set
modalPresentationStyle
to.pageSheet
(default for sheets) or.formSheet
for a narrower appearance on iPad. - Detents: Use
.medium()
(half-screen) and.large()
(near full-screen) to define sheet sizes. iOS 16+ supports custom detents. - Grabber: Set
prefersGrabberVisible
totrue
to show a drag indicator at the top. - Presentation: Use
present(_:animated:completion:)
to display the sheet.
Customizing Sheets
UISheetPresentationController
offers extensive customization options, including detents, edge behavior, and appearance.
Custom Detents (iOS 16+)
Define custom sheet heights using UISheetPresentationController.Detent.custom(identifier:resolver:)
.
if let sheet = sheetViewController.sheetPresentationController {
let customDetent = UISheetPresentationController.Detent.custom(identifier: .init("custom")) { context in
return context.maximumDetentValue * 0.3 // 30% of max height
}
sheet.detents = [customDetent, .medium(), .large()]
sheet.prefersGrabberVisible = true
}
Selected Detent
Set the initial detent using selectedDetentIdentifier
:
sheet.selectedDetentIdentifier = .medium
Edge Behavior
Control how the sheet interacts with edges:
sheet.prefersEdgeAttachedInCompactHeight = true // Attach to bottom in compact height
sheet.prefersScrollingExpandsWhenScrolledToEdge = false // Prevent scrolling from expanding detent
sheet.largestUndimmedDetentIdentifier = .medium // Allow interaction with background at medium detent
Corner Radius and Width
Customize appearance:
sheet.preferredCornerRadius = 20 // Round corners
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true // Respect preferredContentSize
Dismissing Interactively
Sheets are dismissed by dragging down (if prefersGrabberVisible
is true
) or programmatically:
dismiss(animated: true, completion: nil)
Example with Customization
@IBAction func presentCustomSheet(_ sender: UIButton) {
let sheetViewController = SheetViewController()
sheetViewController.modalPresentationStyle = .pageSheet
if let sheet = sheetViewController.sheetPresentationController {
// Custom detent (iOS 16+)
let customDetent = UISheetPresentationController.Detent.custom(identifier: .init("small")) { context in
return context.maximumDetentValue * 0.25
}
sheet.detents = [customDetent, .medium(), .large()]
sheet.selectedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.largestUndimmedDetentIdentifier = .medium
sheet.preferredCornerRadius = 15
}
present(sheetViewController, animated: true, completion: nil)
}
Handling Detent Changes
Observe detent changes using the delegate:
class ViewController: UIViewController, UISheetPresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Set delegate
sheetViewController.sheetPresentationController?.delegate = self
}
func sheetPresentationControllerDidChangeSelectedDetentIdentifier(_ sheetPresentationController: UISheetPresentationController) {
if let detent = sheetPresentationController.selectedDetentIdentifier {
print("Selected detent: \(detent)")
}
}
}
iPad Considerations
On iPad, sheets behave like .formSheet
or .pageSheet
but may require additional configuration:
sheetViewController.preferredContentSize = CGSize(width: 400, height: 600) // Set size for iPad
Best Practices
- Detent Selection: Offer at least two detents (e.g.,
.medium
,.large
) for flexibility. - Grabber: Enable
prefersGrabberVisible
for discoverability of drag-to-dismiss. - Background Interaction: Use
largestUndimmedDetentIdentifier
to allow background interaction when appropriate. - Animation: Ensure smooth transitions by using
animated: true
inpresent
anddismiss
. - Accessibility: Ensure content in the sheet is VoiceOver-compatible and legible at all detent sizes.
- Testing: Test on iPhone and iPad, in various orientations and multitasking modes.
- Minimal Customization: Avoid excessive visual changes to maintain system consistency.
Limitations
- iOS 15+: Sheets via
UISheetPresentationController
are available only on iOS 15 and later. - Custom Detents: Available only on iOS 16 and later.
- iPad Behavior: Sheets may appear as floating windows on iPad; test thoroughly.
- Content Size: Ensure content adapts to dynamic detent heights to avoid clipping.
Summary
Sheets in UIKit, managed by UISheetPresentationController
, provide a modern, flexible way to present modal content. With customizable detents, edge behavior, and appearance, developers can create intuitive and responsive sheet presentations. By following best practices, sheets can enhance the user experience in UIKit-based iOS apps while maintaining system consistency.