Reputation: 283
So I have a minimum target of iOS 16, am using .sheet() with .presentationDetents([.medium]) and it works fine on iOS (iPhone). But when I load it on iPadOS it's always a fullsized sheet, seemingly ignoring the presentation detents. Here is a minimal reproducable code that demonstrates this behaviour.
import SwiftUI
struct ContentView: View {
@State var shouldShowSheet: Bool = false
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
Button("Show Sheet") {
shouldShowSheet.toggle()
}
}
.padding()
.sheet(isPresented: $shouldShowSheet, content: {
VStack {
Text("Some content")
Text("Some more content")
Text("Even more content")
Button("Dismiss Sheet") {
shouldShowSheet.toggle()
}
}
.presentationDetents([.medium])
})
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Below are screenshots of the same code running on iOS and running on iPadOS
I tried using .fraction, .medium, .height and none of them had any effect on iPad sheets. The sheet is always a full-sized one as shown in the image. But on iOS(iPhone) it works as expected.
I'm expecting sheets on iPads to also respect the presentation detents. How can I get different sized sheets on iPad?
Upvotes: 21
Views: 2788
Reputation: 1
Using .sheet()
with .presentationDetents([.medium])
works on iPhone but always appears fullscreen on iPad. This happens because iPadOS treats .sheet()
differently, defaulting to a full-screen modal. Instead, use .popover()
for iPad:
Button("Show Popover") {
showPopover = true
}
.popover(isPresented: $showPopover) {
Text("This is a popover").frame(width: 400, height: 300)
}
Upvotes: 0
Reputation: 2896
If you want to present a medium (or any size) sheet on iPad, here is a workaround. It's not great, but perhaps it's enough for you. Works with iOS16 and up.
@Environment(\.dismiss) var dismiss
to dismiss the sheet in code (if you need that functionality). Instead, you'll need to pass the isPresented
binding to the sheet content and than do isPresented.toggle()
to close the sheet in code.
import Foundation
import SwiftUI
import DeviceKit // https://github.com/devicekit/DeviceKit
struct UniversalSheet: ViewModifier {
@Binding var isPresented: Bool
var onDismiss: (() -> Void)?
var sheetContent: () -> any View
func body(content: Content) -> some View {
ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
content
.if(!Device.current.isPad, transform: {
$0.sheet(isPresented: $isPresented, onDismiss: onDismiss, content: {AnyView(sheetContent())})
})
if Device.current.isPad && isPresented {
VStack(spacing: 0) {
// This adds the close button
HStack {
Spacer()
Button(action: {
isPresented.toggle()
}, label: {
ZStack {
Image(systemName: "xmark.circle.fill")
.resizable()
.scaledToFit()
.font(Font.body.weight(.bold))
.scaleEffect(0.8)
.foregroundColor(Color.gray.opacity(0.6))
}
.frame(width: 35, height: 35)
})
.padding([.top, .trailing], 8)
}
.background(Color(uiColor: .secondarySystemBackground))
// This adds the actual sheet content
AnyView(sheetContent())
.frame(height: 370) // The fixed height of the sheet (on iPad) (excluding the close button)
.transition(.move(edge: .bottom))
}
.frame(width: 450) // The fixed width of the sheet (on iPad)
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding(.bottom)
.onDisappear() {
onDismiss?()
}
}
}
}
}
extension View {
func universalSheet(isPresented: Binding, onDismiss: (() -> Void)?, content: @escaping () -> any View) -> some View {
self.modifier(UniversalSheet(isPresented: isPresented, onDismiss: onDismiss, sheetContent: content))
}
@ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
Note: universalSheet(isPresented: Binding
should be universalSheet(isPresented: Binding<Bool>
(somehow the codeblock can't handle < and > (let me know if you know how to change that).
USAGE: You use it just as you would the .sheet modifier:
.universalSheet(isPresented: $presentSheet, onDismiss: {
somethingYouWantToExecuteOnSheetDismiss()
}, content: {
YourSheetView(presentSheet: $presentSheet)
})
Upvotes: 0
Reputation: 637
You might be looking for the new presentationSizing(_:) API that is available in iOS 18+.
It allows configuring the shape and dimension of the modal presented sheet. From there you may build your own detent behavior on a regular-regular size class window such as full screen iPad.
Upvotes: 0