How to control the size of presented views
How to control the size of presented views 관련
Updated for Xcode 16
Improved in iOS 18
SwiftUI has a dedicated presentationSizing()
modifier that gives us fine-grained control over how presented views are sized on the screen.
This is different from the presentationDetents()
modifier that allows us to create bottom sheets and similar – presentationSizing()
is for controlling the shape of the view.
For example, we could make a DetailView
struct such as this one
struct DetailView: View {
var body: some View {
Text("Detail View")
.font(.largeTitle)
.presentationSizing(.form)
}
}
That uses the .form
setting, which is one of the built-in sizes – on iPhone it will just be a regular sheet, but on iPad it's a large square shape that's centered neatly.
You don't need to do anything special to present a view sized this way; just using the regular sheet()
modifier is fine, like this:
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Detail View") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet, content: DetailView.init)
}
}
Another great option for presentationSizing()
is .fitted
, which sizes the sheet according to its content. Even better, this can be added to other sizes: you can use .form.fitted(horizontal: true, vertical: false)
to mean "start with the form size, but shrink horizontally to fit my content".
If you have more complex layouts, try using sticky sizing to tell SwiftUI to expand the sheet automatically in one or both dimensions based on the size of your content. It will still honor the minimum size you request, but it has that potential to grow as needed.
The best way to understand sticky sizing is to try it yourself. In the code below, tapping the button adds more text to content
, and it will eventually cause the sheet to expand because of that sticky sizing:
struct ContentView: View {
@State private var content = "This is some very important content."
var body: some View {
VStack {
Text(content)
.fixedSize(horizontal: false, vertical: true)
Button("Tap to expand") {
content += "This is more important content. This is more important content. This is more important content. This is more important content."
}
}
.font(.largeTitle)
.presentationSizing(.form.sticky())
}
}
Tips
I used fixedSize()
on the text to stop it from clipping.
You can go even further if you want – you could use fitted and sticky sizing.
Where things get really interesting is the PresentationSizing
protocol, which lets you create your own sizes. Adopting this protocol means adding just one method that asks you to propose a size for some content.
For example, we could make a PaddedSizing
type that asks its contents for their ideal size, then adds 100 points of extra width and height:
struct PaddedSizing: PresentationSizing {
func proposedSize(for root: PresentationSizingRoot, context: PresentationSizingContext) -> ProposedViewSize {
let size = root.sizeThatFits(.unspecified)
return ProposedViewSize(width: size.width + 100, height: size.height + 100)
}
}
It's not required, but adding a small extension on PresentationSizing
makes this new sizing type easier to use:
extension PresentationSizing where Self == PaddedSizing {
static var padded: Self {
PaddedSizing()
}
}
And now we can use that by adding .presentationSizing(.padded)
to any sheet content.