Expanded surfaces
A takeover on the notch, for something that needs the whole shelf.
An expanded surface replaces the shelf's normal contents with yours: a picker, a form, a full view of something a widget can only summarise. It is the biggest thing a droplet can put on screen, and the one that most needs restraint — while it is up, the user cannot see anything else Droppy would have shown.
Requires the expanded-surface capability.
Declaring one
extension WorldClockDroplet: ExpandedSurfaceProviding {
public var expandedSurfaces: [ExpandedSurfaceDescriptor] {
[
ExpandedSurfaceDescriptor(
id: "clocks-detail",
title: "World Clock",
systemImage: "globe",
suppresses: [.shelfWidgets, .autoCollapse]
)
]
}
}What it suppresses
ExpandedSurfaceHostSuppression is the host chrome your surface turns off while it is presented. Ask for the least you need:
| Option | Turns off |
|---|---|
shelfWidgets | The normal widget row |
favoritesBar | The favourites strip |
floatingNavLane | The floating navigation lane |
notificationBanners | Banners over the shelf |
autoCollapse | The shelf closing on its own |
outsideClickDismissal | Clicking away closing it |
swipeGestures | Swipe-to-close |
takeover is the full set. Reach for it only when a stray click really would lose the user's work: suppressing outsideClickDismissal means the obvious way out stops working, and a surface that traps someone is worse than one that closes early.
Sizing
public func expandedSurfaceSize(
_ id: ExpandedSurfaceID,
fitting proposal: ExpandedSurfaceSizeProposal
) -> CGSize? {
CGSize(width: proposal.standardSize.width, height: CGFloat(clocks.count) * 44 + 64)
}Return nil to take proposal.standardSize. The proposal also carries maximumSize, the surface kind, and the display, so a surface can be shorter on a small screen.
Presenting and dismissing
let presentation = host.notchSurface.presentExpandedSurface(
ExpandedSurfacePresentationRequest(surfaceID: "clocks-detail", opensShelf: true)
)ExpandedSurfacePresentation identifies the presentation, not the surface, and the difference matters. Tearing a takeover down runs after the close spring settles, and by then the user may have summoned the same surface again — a teardown keyed on the surface id would kill the fresh one. Capture the presentation you started with and compare before acting on a delayed callback.
expandedSurfaceDidDismiss(_:presentation:reason:) is called once per presentation, for every reason including your own dismissal. ExpandedSurfaceDismissalReason tells you which: the user collapsed the shelf, another surface pre-empted yours, the display changed, the shelf was turned off, or host policy ended it.