Priva28 / HostingPassthrough

Let SwiftUI and UIKit views live in harmony.
MIT License
89 stars 4 forks source link

Allow working with Interface Builder #2

Open alobaili opened 1 year ago

alobaili commented 1 year ago

Thanks for this brilliant solution.

It would be awesome if it can also work with view controllers that use Interface Builder. I tested this library on a view controller that have some IB outlets from an XIB and once I make the view controller a subclass of HostingParentController, all outlets become nil.

GerdC commented 1 year ago

@alobaili this code works for me with storyboard. Please test thoroughly and give feedback. (I also changed func viewWillLayoutSubviews() to open.)

open class HostingParentController: UIViewController {

    public var makeBackgroundsClear = true

    /// If the touches land on the base view of the HostingParentController, they will be forwarded to this view if it is not nil.
    public var forwardBaseTouchesTo: UIView?

    /// If the touches land on the bottom of a SwiftUI scroll container (*not* the content), pass through these touches to the UIKit layer underneath.
    public var ignoreTouchesOnSwiftUIScrollView = false

    override open func loadView() {
        if isLoadedFromStoryboard {
            super.loadView()  // Load the view from the storyboard
        } else {
            let capturer = HostingParentView()
            view = capturer
        }
    }

    private var isLoadedFromStoryboard: Bool {
        return storyboard != nil
    }

    private func wrapOriginalViewWithHostingParentView() {
        let originalView = self.view!

        let capturer = HostingParentView(frame: originalView.frame)
        self.view = capturer

        capturer.addSubview(originalView)
        originalView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            originalView.topAnchor.constraint(equalTo: capturer.topAnchor),
            originalView.bottomAnchor.constraint(equalTo: capturer.bottomAnchor),
            originalView.leadingAnchor.constraint(equalTo: capturer.leadingAnchor),
            originalView.trailingAnchor.constraint(equalTo: capturer.trailingAnchor)
        ])
    }

    open override func viewDidLoad() {
        super.viewDidLoad()

        // Only wrap the view if loaded from a storyboard
        if isLoadedFromStoryboard {
            wrapOriginalViewWithHostingParentView()
        }
    }

    open override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        if let capturer = view as? HostingParentView {
            capturer.makeBackgroundsClear = makeBackgroundsClear
            capturer.forwardBaseTouchesTo = forwardBaseTouchesTo
            capturer.ignoreTouchesOnSwiftUIScrollView = ignoreTouchesOnSwiftUIScrollView
        }
    }
}
GerdC commented 1 year ago

Using the code above intensely for a few days now and it is absolutely stable for me.