Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!
If you’ve worked with the sheet
modifier on iPad, you cannot control the presentation size of a modal sheet with SwiftUI. And the sheet gets too big on a 12.9” screen iPad.
There is a question on Stack Overflow that asks about SwiftUI sheet() modals with custom size on iPad. The most rated answer provides you with a custom formSheet
modifier.
UIKit Navigation
For my use case, however, I’m using UIKit navigation. So, it made sense to explore an option where I can use the existing way of calling the UIViewController.present(_:animated:completion:)
method. It uses modalTransitionStyle
and modalPresentationStyle
.
Custom UIHostingController
To start with, we create a custom UIHostingController
with clear background color and custom transition and presentation style. You can also make it more generic by accepting the styles as a parameter:
class UISheetController<Content>: UIHostingController<Content> where Content: View {
override init(rootView: Content) {
super.init(rootView: rootView)
view.backgroundColor = .clear
modalPresentationStyle = .overCurrentContext
modalTransitionStyle = .crossDissolve
}
@available(*, unavailable)
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
CustomSheetView
Then, we create a custom sheet view. It takes a view as a parameter and will dismiss the sheet if you tap outside the view. It takes the custom size as ratios of the whole screen:
struct CustomSheetView<Content: View>: View {
@Environment(\.dismiss) private var dismiss
private var content: Content
private var heightRatio: CGFloat
private var widthRatio: CGFloat
init(height: CGFloat = 0.7, width: CGFloat = 0.7, @ViewBuilder content: @escaping () -> Content) {
self.widthRatio = width
self.heightRatio = height
self.content = content()
}
var body: some View {
ZStack {
Color.black.opacity(0.5).onTapGesture { dismiss() }
GeometryReader { geometry in
let width = geometry.size.width * widthRatio
let height = geometry.size.height * heightRatio
content
.position(x: geometry.frame(in: .global).midX,
y: geometry.frame(in: .global).midY)
.frame(width: width, height: height, alignment: .center)
}
}
.edgesIgnoringSafeArea(.all)
}
}
The ZStack
has a Color
that creates the effect of darkening the view behind the sheet, and the content is positioned in the middle.
Usage
You can use it like:
func presentAlert(for error: Error) {
let view = CustomSheetView(height: 0.4, width: 0.3) { CustomAlertView(error: error) }
let controller = UISheetController(rootView: view)
navigationController?.present(controller, animated: true)
}
Conclusion
You can use the native way of creating a similar modifier like sheet
to get a custom sheet. If you’re using UIKit navigation, you may prefer to use the solution mentioned in this article.
If you have a better approach, please tag @rudrankriyam on Twitter! I love constructive feedback and appreciate constructive criticism.
Thanks for reading, and I hope you’re enjoying it!
Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!