Using UIHostingConfiguration Without Cell Views

Jonathan Hemi · March 30, 2023

UIHostingConfiguration was introduced in WWDC22 and rightly hyped as a great way for UIKit Collection and Table Views to benefit from SwiftUI’s composability and ease of use by embedding directly inside cell views. However, nothing in content configuration is limited to cell views. We can embed them in any old UIView by using the main method from UIContentConfiguration protocol, makeContentView.

What About UIHostingController?

UIHostingController was and still is the indispensable tool for using SwiftUI at the view controller level (e.g. in a scene delegate or as a modal presented from another view controller). However it gives us an opaque UIView which we can’t subclass and can’t override methods in.

So What Can We Use This For?

Adding a UIHostingConfiguration to a UIView subclass we control let’s us override methods, e.g. for hit testing or hit testing. For example, we can (“finally”) create a partially-transparent SwiftUI overlay over a UIKit context, while allowing the UIKit parts behind to receive touch events.

We can use something like this:

final class HitTestingHostingView<Content: View>: UIView {
	let contentView: UIView

	init(content: () -> Content) {
		let config = UIHostingConfiguration(content: content)
		let contentView = config.makeContentView()
		self.contentView = contentView
		super.init(frame: .zero)
		translatesAutoresizingMaskIntoConstraints = false
		contentView.translatesAutoresizingMaskIntoConstraints = false
		addSubview(contentView)
		NSLayoutConstraint.activate([
			contentView.topAnchor.constraint(equalTo: topAnchor),
			contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
			contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
			contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
		])
	}

	override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
		let hitTestView = super.hitTest(point, with: event)
		if let hitTestView {
			// Do something according to the view, e.g. return nil to ignore the event
		}
		return hitTestView
	}
}

A Trend for UIKit

UIListContentConfiguration, introduced in iOS 14, also conforms to UIContentConfiguration and so we can also add a standard list “cell” view to any arbitrary UIView. Together with the fantastic UIButton.Configuration and others, we’re helpfully guiding us away from subclassing and coupling into a more composable future where UIKit still lives side by side with SwiftUI while learning some cool new tricks. Here’s hoping for more of that in WWDC23.