I have an ExpandingView
like this:
struct ExpandingView: View {
@State var isExpanded = false
var body: some View {
RoundedRectangle(cornerRadius: 16.0)
.fill(Color.accentColor.opacity(0.1))
.overlay {
Button("Toggle expand") {
withAnimation {
isExpanded.toggle()
}
}
}
.frame(height: isExpanded ? 500 : 100)
}
}
struct ContentView: View {
var body: some View {
ExpandingView()
.border(Color.red)
.padding()
}
}
Result:
This works fine. However, due to performance reasons, I am experimenting with embedding ExpandingView
within a intermediary UIHostingController
instead of having it inline. This way, I have more control over how it's rendered.
struct WrapperHostingControllerRepresentable<Content: View>: UIViewControllerRepresentable {
@ViewBuilder var content: Content
func makeUIViewController(context: Context) -> UIHostingController<Content> {
UIHostingController(rootView: content)
}
func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {
uiViewController.rootView = content
}
func sizeThatFits(_ proposal: ProposedViewSize, uiViewController: UIHostingController<Content>, context: Context) -> CGSize? {
let canvas = CGSize(width: proposal.width ?? 0, height: 5000)
let size = uiViewController.sizeThatFits(in: canvas)
print("Calculating size: \(size)")
return size
}
}
struct ContentView: View {
var body: some View {
WrapperHostingControllerRepresentable {
ExpandingView()
}
.border(Color.red)
.padding()
}
}
Here's the result:
As you can see, the red border is not recalculated and Calculating size: (370.0, 100.0)
is printed only once in the console. How do I get the UIViewRepresentable to resize along with its contents?
You should set sizingOptions
to .preferredContentSize
and/or .intrinsicContentSize
:
func makeUIViewController(context: Context) -> UIHostingController<Content> {
let host = UIHostingController(rootView: content)
host.sizingOptions = .preferredContentSize
return host
}
Both options work in this case, because they both cause the hosting controller to track changes in the SwiftUI view's ideal size. The difference is whether the ideal size is reflected in the view controller's preferredContentSize
or intrinsicContentSize
. A change in either of these will cause sizeThatFits
to be called.