I have a custom UIViewControllerRepresentable (layout-related code shown below). This tries to replicate the native SwiftUI ScrollView, except it scrolls from the bottom except the top.
View hierarchy
view: UIView
|
\- scrollView: UIScrollView
   |
   \- innerView: UIView
      |
      \- hostingController.view: SwiftUI hosting view
This all works as intended when the view is initialized. The hosting view is populated with its contents, and the constraints make sure that the scroll view's contentSize is set properly.
However, when the contents of the hosting view changes, the hostingController.view doesn't resize to fit its contents.
Green: As intended, the scroll view matches the size of the hosting view controller.
Blue: The hosting view itself. It keeps the size it had when it was first loaded, and doesn't expend as it should.
Red: A stack view within the hosting view. In this screenshot, content was been added to the stack, causing it to expand. You can see the difference in size as a result.
The UIHostingController (blue) should expand to fit its contents (red).
The scroll view's content size is not explicitly set, because this is handled by auto layout.
Constraint code is shown below, if it helps.
class UIBottomScrollViewController<Content: View>: UIViewController, UIScrollViewDelegate {
    var hostingController: UIHostingController<Content>! = nil
    init(rootView: Content) {
        self.hostingController = UIHostingController<Content>(rootView: rootView)
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    var scrollView: UIScrollView = UIScrollView()
    var innerView = UIView()
    override func loadView() {
        self.view = UIView()
        self.addChild(hostingController)
        view.addSubview(scrollView)
        scrollView.addSubview(innerView)
        innerView.addSubview(hostingController.view)
        scrollView.delegate = self
        scrollView.scrollsToTop = true
        scrollView.isScrollEnabled = true
        scrollView.clipsToBounds = false
        scrollView.layoutMargins = .zero
        scrollView.preservesSuperviewLayoutMargins = true
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        innerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        innerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        innerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        innerView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
        innerView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
        innerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        hostingController.view.topAnchor.constraint(equalTo: innerView.topAnchor).isActive = true
        hostingController.view.leftAnchor.constraint(equalTo: innerView.leftAnchor).isActive = true
        hostingController.view.rightAnchor.constraint(equalTo: innerView.rightAnchor).isActive = true
        hostingController.view.bottomAnchor.constraint(equalTo: innerView.bottomAnchor).isActive = true
        hostingController.view.autoresizingMask = []
        hostingController.view.layoutMargins = .zero
        hostingController.view.insetsLayoutMarginsFromSafeArea = false
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.autoresizingMask = []
        scrollView.layoutMargins = .zero
        scrollView.insetsLayoutMarginsFromSafeArea = false
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        innerView.autoresizingMask = []
        innerView.layoutMargins = .zero
        innerView.insetsLayoutMarginsFromSafeArea = false
        innerView.translatesAutoresizingMaskIntoConstraints = false
        hostingController.didMove(toParent: self)
        scrollView.keyboardDismissMode = .interactive
    }
}
struct BottomScrollView<Content: View>: UIViewControllerRepresentable {
    var content: () -> Content
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    func makeUIViewController(context: Context) -> UIBottomScrollViewController<Content> {
        let vc = UIBottomScrollViewController(rootView: self.content())
        return vc
    }
    func updateUIViewController(_ viewController: UIBottomScrollViewController<Content>, context: Context) {
        viewController.hostingController.rootView = self.content()
    }
}
