Sheets, Half Sheets & more with Presentation Detents in SwiftUI

kuncans

Duncan Kent

Posted on September 20, 2022

Sheets, Half Sheets & more with Presentation Detents in SwiftUI

Apple added Detents to their Presentation Containers in WWDC2022, in order to provide more flexibility among various parts of your app's view hierarchy.

The new PresentationDetent struct comes with 2 included properties:

  • medium - the sheet takes up approximately half of the device's screen
  • large - the sheet covers the entire screen

medium

Image description

large

Image description

These detents can be implemented using the .presentationDetents modifier on a sheet. You can choose to include multiple options using a Set of type PresentationDetent.

.sheet(isPresented: $sheetPresented) {
    SheetView()
        .presentationDetents([.medium, .large])
}
Enter fullscreen mode Exit fullscreen mode

There are a few documented methods also, that allow for greater flexibility when using sheet.

This means that it would be very straightforward to create the /bottom drawer/ effect that is becoming more popular in apps using the following two methods:

  • fraction - allows you to specify a fractional height (of the screen)

  • height- specify a height that your sheet will occupy (relative to the bottom of the screen)

Both methods use the type CGFloat, and can be assigned to variables and changed programmatically, altering the placement of your sheet each time it appears in the app session.

Image description

A custom PresentationDetent can be created which must conform to the protocol CustomPresentationDetent . In this example, I have created a detent specifically to act in a "drawer" presentation style.

DrawerDetent Struct and Extension to PresentationDetent

private struct DrawerDetent: CustomPresentationDetent {
    static func height(in context: Context) -> CGFloat? {
        max(80, context.maxDetentValue * 0.15)
    }
}
Enter fullscreen mode Exit fullscreen mode
extension PresentationDetent {
    static let drawer = Self.custom(DrawerDetent.self)
}
Enter fullscreen mode Exit fullscreen mode

Calling this on the view, with the appropriate modifier:

.sheet(isPresented: $sheetPresented)
    SheetView()
        .presentationDetents([.drawer])
}
Enter fullscreen mode Exit fullscreen mode

Drawer Style Sheet Result:

Image description

If you'd prefer to have a range of different sizes, and allow the user a lot more control over the placement of the sheet, you can include many options in the .presentationDetents Set.

let heights = stride(from: 0.2, through: 1.0, by: 0.2).map {
    PresentationDetent.fraction($0)
}
Enter fullscreen mode Exit fullscreen mode
.sheet(isPresented: $sheetPresented) {
    SheetView()
        .presentationDetents(Set(heights))
}
Enter fullscreen mode Exit fullscreen mode

Lastly, you can combine these previous examples, for even more bespoke sheet options using the union function of Set:

SheetView()
    .presentationDetents(Set(heights).union([.drawer, .medium]))
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
kuncans
Duncan Kent

Posted on September 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related