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

2

7

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

Greathouse answered 17/2, 2021 at 5:51 Comment(0)
H
1

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)
Hostile answered 22/10, 2021 at 17:0 Comment(1)
Yes, I didn't mention it, but I fixed it that way.Daddylonglegs
D
5

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()
        }
    }
}
Disclaim answered 17/2, 2021 at 5:56 Comment(8)
With this workaround, the screen size and Swift UI View size will be the same. But I want the size of Swift UI View to be self-sizing.Daddylonglegs
can you add an image for what you want?Disclaim
I want to make it look like the attached iOS 14 image. i.sstatic.net/Q2SUq.jpgDaddylonglegs
I added description & ScreenShot. Please check it.Daddylonglegs
@RajaKishan is totally right - you have to add top + bottom + leading + trailing constraints.Blimey
if add top + bottom + leading + trailing constraints, SampleView's size is equal to ViewController.view's size. But I want SampleView to decide its own size. I don't want it to be displayed large in the center. I want to display in the minimum required size.Daddylonglegs
UIViewController.view doesn't have any default size/constraints, so if you add child view with all constraints then this child will decide the size of parent view (you can test this with UIKit views). However in your case I think there is a bug in iOS 13. UIStackView doesn't listen to SwiftUI views changes. I tried several approaches and nothing worked, but everything works fine on iOS 14.Blimey
Yes, I also think it's a bug in iOS 13.There is no direct solution to this problem, so I'm looking for a workaround.Daddylonglegs
H
1

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)
Hostile answered 22/10, 2021 at 17:0 Comment(1)
Yes, I didn't mention it, but I fixed it that way.Daddylonglegs

© 2022 - 2024 — McMap. All rights reserved.