I need to present a sheet containing SwiftUI view from UIViewController, but the height of the sheet has to fit the height of the content of the SwiftUI view.
I know that there is a possibility to specify custom detent for the height of the sheet, but I wonder whether it is possible to get the height of the content size of SwiftUI view when presenting it from the UIViewController?
So far I have tried getting the content size with the help of the GeometryReader in SwiftUI view, but did not get any result.
Here is my code where the SwiftUI view is presented for reference:
func presentSwiftUIPageSheet() {
let swiftUIView = SwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
hostingController.modalPresentationStyle = .pageSheet
hostingController.isModalInPresentation = false
hostingController.sheetPresentationController?.detents = [.medium(), .large()]
present(hostingController, animated: true)
}
Any advice would be much appreciated!
For SwiftUI only code there is a question & answer here: Make sheet the exact size of the content inside Based on that, the iOS 16.0+ solution for presenting from an UIViewcontroller would be:
func presentSwiftUIPageSheet() {
var sheetHeight = CGFloat.zero
let swiftUIView = SwiftUIView()
.readSize { sheetHeight = $0.height }
let hostingController = UIHostingController(rootView: swiftUIView)
hostingController.modalPresentationStyle = .pageSheet
hostingController.isModalInPresentation = false
hostingController.sheetPresentationController?.detents = [.custom { _ in 0 },
.large()]
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
hostingController.sheetPresentationController?.animateChanges {
hostingController.sheetPresentationController?.detents = [.custom { _ in sheetHeight },
.large()]
}
}
present(hostingController, animated: true)
}
with SwiftUI extension:
private struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() }
}
extension View {
func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
background(
GeometryReader { proxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: proxy.size)
}
).onPreferenceChange(SizePreferenceKey.self, perform: onChange)
}
}