On iOS 15, the UIHostingController is adding some weird extra padding to its hosting SwiftUI view (_UIHostingView)
Asked Answered
K

3

21

UPDATE: 2022-09-26

This issue has been fixed on iOS 16. Although the issue is still present on iOS 15 even when the project is compiled with the iOS 16 SDK.

Original question:

On iOS 15, the UIHostingController is adding some weird extra padding to its hosting SwiftUI view (_UIHostingView).

See screenshot below (Blue = extra space, Red = actual view’s):

enter image description here

Does anyone know why this happens?

I've reported this bug, Apple folks: FB9641883

PD: I have a working project reproducing the issue which I attached to the Feedback Assistant issue. If anyone wants it I can upload it too.

Kabob answered 21/9, 2021 at 8:44 Comment(0)
A
28

I found out that subclassing UIHostingController as follows fixes the issue with extra padding:

final class HostingController<Content: View>: UIHostingController<Content> {
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        view.setNeedsUpdateConstraints()
    }
}

It also fixes a problem of the UIHostingController not resizing correctly when its SwiftUI View changes size.

Aunt answered 28/9, 2021 at 9:39 Comment(7)
wow this does work :D Why I didn't think of this one, very smart to try, thanks Sebastian!Kabob
I'm trying to create a UIImage from the SwiftUI View and the above still results in a offset/clipped image.Puddle
This didn’t work for meAntihalation
It doesn't work if the Content view gets larger.Soulier
be sure to add the hosting view controller as a child to the VC as well as a didMove(toParent: self)Therese
It's never a good idea to modify the view's layout in the viewDidLayoutSubviews, you can easily end up with an infinite loopSpitler
This does not work for mePorte
K
6

I’ve tried to find why is this happening without luck. The only thing I’ve found to fix it is setting a height constraint to its intrinsic content size in a subclass of UIHostingController:

    private var heightConstraint: NSLayoutConstraint?

    override open func viewDidLoad() {
        super.viewDidLoad()
        if #available(iOS 15.0, *) {
            heightConstraint = view.heightAnchor.constraint(equalToConstant: view.intrinsicContentSize.height)
            NSLayoutConstraint.activate([
                heightConstraint!,
            ])
        }
    }

    override open func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        heightConstraint?.constant = view.intrinsicContentSize.height
    }
Kabob answered 21/9, 2021 at 8:44 Comment(2)
This one actually worked for me 100%. The accepted answer is flakey in certain situations.Turnsole
This does not work for mePorte
M
0

In my project, the same issue applied, but it was due to safe area insets becoming wrong when scrolling. To solve it, I changed the safe area insets in a UIHostingController subclass whenever they were changed by iOS:

class OverrideSafeAreaTopAndBottomInsetHostingController<ContentView: SwiftUI.View>: UIHostingController<ContentView> {

     override func viewSafeAreaInsetsDidChange() {
         super.viewSafeAreaInsetsDidChange()

         if view.safeAreaInsets.bottom > 0 || view.safeAreaInsets.top > 0 {
             additionalSafeAreaInsets = .init(top: -view.safeAreaInsets.top, left: 0, bottom: -view.safeAreaInsets.bottom, right: 0)
        }
    }
}

Credits: https://stackoverflow.com/questions/70032739#73145761

Mcpeak answered 13/12, 2023 at 13:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.