Swift UI: UIHostingController.view is not fit to content view size at iOS 13

Viewed 2563

iOS 13 iOS 14

I want to update Swift UI View according to the communication result. But UIHostingController.view is not fit rootView size at iOS 13. The same thing happens when I try with the sample code below. I want to add self-sizing SwiftUI View to UIStackView, but SwiftUI View overlaps with the previous and next views is occurring because this problem. How can I avoid this problem?

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let object = SampleObject()
        let sampleView = SampleView(object: object)
        let hosting = UIHostingController(rootView: sampleView)
        hosting.view.backgroundColor = UIColor.green
        addChild(hosting)
        view.addSubview(hosting.view)
        hosting.view.translatesAutoresizingMaskIntoConstraints = false
        hosting.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        hosting.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        hosting.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        hosting.didMove(toParent: self)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            object.test()
        }
    }
}


struct SampleView: View {
    @ObservedObject var object: SampleObject
    
    var body: some View {
        VStack {
            Text("test1").background(Color.blue)
            Text("test2").background(Color.red)
            if object.state.isVisibleText {
                Text("test2").background(Color.gray)
            }
        }
        .padding(32)
        .background(Color.yellow)
    }
}


final class SampleObject: ObservableObject {
    struct ViewState {
        var isVisibleText: Bool = false
    }
    
    @Published private(set) var state = ViewState()
    
    func test() {
        state.isVisibleText = true
    }
}

If addSubview to UIStackView as below, the height of Swift UI View will not change in iOS13. enter image description here

iOS13 (incorrect) enter image description here

iOS14 (correct) enter image description here

2 Answers

You have not set the bottom anchor, add this line

hosting.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

Another easy way is to set frame to hosting controller view and remove the constraint.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let object = SampleObject()
        let sampleView = SampleView(object: object)
        let hosting = UIHostingController(rootView: sampleView)
        hosting.view.frame = UIScreen.main.bounds //<---here
        hosting.view.backgroundColor = UIColor.green
        addChild(hosting)
        view.addSubview(hosting.view)
        hosting.didMove(toParent: self)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            object.test()
        }
    }
}

Only for iOS 13

Try this:

Every time the view size change, call this:

sampleView.view.removeFromSuperview()
let sampleView = SampleView(object: object)
let hosting = UIHostingController(rootView: sampleView)
view.addArrangedSubview(hosting.view)
Related