Ios UIHostingController应展开以适应内容

Ios UIHostingController应展开以适应内容,ios,swift,swiftui,ios-autolayout,uihostingcontroller,Ios,Swift,Swiftui,Ios Autolayout,Uihostingcontroller,我有一个自定义的UIViewControllerRepresentable(与布局相关的代码如下所示)。这将尝试复制本机SwiftUIScrollView,但它从底部(顶部除外)滚动 视图层次结构 当视图初始化时,这一切都可以正常工作。宿主视图将填充其内容,这些约束确保滚动视图的contentSize设置正确 但是,当宿主视图的内容更改时,hostingController.view不会调整大小以适应其内容 绿色:按预期,滚动视图与宿主视图控制器的大小相匹配 蓝色:宿主视图本身。它保持了第一次

我有一个自定义的
UIViewControllerRepresentable
(与布局相关的代码如下所示)。这将尝试复制本机SwiftUI
ScrollView
,但它从底部(顶部除外)滚动

视图层次结构 当视图初始化时,这一切都可以正常工作。宿主视图将填充其内容,这些约束确保滚动视图的
contentSize
设置正确

但是,当宿主视图的内容更改时,
hostingController.view
不会调整大小以适应其内容

绿色:按预期,滚动视图与宿主视图控制器的大小相匹配

蓝色:宿主视图本身。它保持了第一次加载时的大小,并且不会像应该的那样消耗

红色:宿主视图中的堆栈视图。在这个屏幕截图中,内容被添加到堆栈中,导致堆栈扩展。因此,您可以看到大小的差异

UIHostingController(蓝色)应展开以适合其内容(红色)。 滚动视图的内容大小没有明确设置,因为这是由自动布局处理的

约束代码如下所示(如果有帮助的话)

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()
    }
}
类UIViewController:UIViewController、UIScrollViewDelegate{ var hostingController:UIHostingController!=nil init(rootView:Content){ self.hostingController=UIHostingController(rootView:rootView) super.init(nibName:nil,bundle:nil) } 必需初始化?(编码器:NSCoder){ fatalError(“初始化(编码者:)尚未实现”) } var scrollView:UIScrollView=UIScrollView() var innerView=UIView() 重写func loadView(){ self.view=UIView() self.addChild(主机控制器) view.addSubview(滚动视图) scrollView.addSubview(内部视图) innerView.addSubview(hostingController.view) scrollView.delegate=self scrollView.scrollsToTop=true scrollView.IsCrollenabled=true scrollView.clipsToBounds=false scrollView.layoutMargins=.0 scrollView.PreserveSPerviewLayoutMargins=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=.0 hostingController.view.insetsLayoutMarginsFromSafeArea=false hostingController.view.translatesAutoresizingMaskIntoConstraints=false scrollView.autoresizingMask=[] scrollView.layoutMargins=.0 scrollView.InsettsLayoutMarginsFromSafeArea=false scrollView.TranslatesAutoResizezingMaskintoConstraints=false innerView.autoresizingMask=[] innerView.layoutMargins=.0 innerView.insetsLayoutMarginsFromSafeArea=false innerView.TranslatesAutoResizezingMaskintoConstraints=false hostingController.didMove(toParent:self) scrollView.keyboardDismissMode=.interactive } } 结构底部滚动视图:UIViewControllerRepresentable{ 变量内容:()->内容 初始化(@ViewBuilder内容:@escaping()->content){ self.content=内容 } func makeUIViewController(上下文:context)->UIBottomScrollViewController{ 让vc=UIBottomScrollViewController(rootView:self.content()) 返回vc } func updateUIViewController(viewController:UIBottomScrollViewController,上下文:上下文){ viewController.hostingController.rootView=self.content() } }
我在类似的ish视图层次结构中遇到了同样的问题,涉及到
UIHostingController
和滚动视图,并发现了一个难看的漏洞使其正常工作。基本上,我添加了高度约束并手动更新常量:

private var heightConstraint: NSLayoutConstraint?

...

override func viewDidLoad() {
    ...


    heightConstraint = viewHost.view.heightAnchor.constraint(equalToConstant: 0)

    ...
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // This plays off what @Rengers was saying, but wanted to include my solution that took me a fair amount of time to figure out.

Hopefully save some time

struct SizingView<T: View>: View {
    
    let view: T
    let updateSizeHandler: ((_ size: CGSize) -> Void)
    init(view: T, updateSizeHandler: @escaping (_ size: CGSize) -> Void) {
        self.view = view
        self.updateSizeHandler = updateSizeHandler
    }
    var body: some View {
        view.background(
            GeometryReader { proxy in
                Color.clear
                    .preference(key: SizePreferenceKey.self, value: proxy.size)
            }
        )
        .onPreferenceChange(SizePreferenceKey.self) { preferences in
            updateSizeHandler(preferences)
        }

    }
    
    func size(with view: T, geometry: GeometryProxy) -> T {
        updateSizeHandler?(geometry.size)
        return view
    }
}
private var heightConstraint:NSLayoutConstraint?
...
重写func viewDidLoad(){
...
heightConstraint=viewHost.view.heightAnchor.constraint(equalToConstant:0)
...
}
重写func viewdilayoutsubviews(){
super.viewDidLayoutSubviews()

//这是在重复@Rengers所说的话,但想包括我花了相当长时间才弄明白的解决方案

希望能节省一些时间

struct SizingView:View{
让我们来看一看:T
let updateSizeHandler:((size:CGSize)->Void)
init(视图:T,updateSizeHandler:@escaping(size:CGSize)->Void){
self.view=view
self.updateSizeHandler=updateSizeHandler
}
var body:一些观点{
view.background(
GeometryReader{中的代理
颜色。清晰
.preference(键:SizePreferenceKey.self,值:proxy.size)
}
)
.onPreferenceChange(大小)
private var heightConstraint: NSLayoutConstraint?

...

override func viewDidLoad() {
    ...


    heightConstraint = viewHost.view.heightAnchor.constraint(equalToConstant: 0)

    ...
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // This plays off what @Rengers was saying, but wanted to include my solution that took me a fair amount of time to figure out.

Hopefully save some time

struct SizingView<T: View>: View {
    
    let view: T
    let updateSizeHandler: ((_ size: CGSize) -> Void)
    init(view: T, updateSizeHandler: @escaping (_ size: CGSize) -> Void) {
        self.view = view
        self.updateSizeHandler = updateSizeHandler
    }
    var body: some View {
        view.background(
            GeometryReader { proxy in
                Color.clear
                    .preference(key: SizePreferenceKey.self, value: proxy.size)
            }
        )
        .onPreferenceChange(SizePreferenceKey.self) { preferences in
            updateSizeHandler(preferences)
        }

    }
    
    func size(with view: T, geometry: GeometryProxy) -> T {
        updateSizeHandler?(geometry.size)
        return view
    }
}