Skip to content

Instantly share code, notes, and snippets.

@simonbs
Last active February 7, 2026 13:51
Show Gist options
  • Select an option

  • Save simonbs/457725e218fc85fc765ed770815314df to your computer and use it in GitHub Desktop.

Select an option

Save simonbs/457725e218fc85fc765ed770815314df to your computer and use it in GitHub Desktop.
In iOS 16, Apple added UIHostingConfiguration, making it straightforward to use a SwiftUI view in instances of UICollectionViewCell and UITableViewCell. This project shows how UIHostingConfiguration can be backported to iOS 14 by implementing a type that conforms to the UIContentConfiguration protocol.

SwiftUIHostingConfiguration

In iOS 16, Apple added UIHostingConfiguration, making it straightforward to use a SwiftUI view in instances of UICollectionViewCell and UITableViewCell. This gist shows how UIHostingConfiguration can be backported to iOS 14 by implementing a type that conforms to UIContentConfiguration.

Starting from iOS 16, we can use SwiftUI views in an instance of UICollectionView or UITableViewCell like this:

cell.contentConfiguration = UIHostingConfiguration {
    ExampleCellContentView(color: Color(item.color), text: item.text)
}

With the SwiftUIHostingConfiguration, we can use a similar API starting from iOS 14.

cell.contentConfiguration = SwiftUIHostingConfiguration(parentViewController: self) {
    ExampleCellContentView(color: Color(item.color), text: item.text)
}

We pass an instance of our view controller because, under the hood, the SwiftUIHostingConfiguration creates a UIHostingController and inserts it into the view controller hierarchy.

video.mov
import SwiftUI
import UIKit
struct SwiftUIHostingConfiguration<Content: View>: UIContentConfiguration {
private weak var parentViewController: UIViewController?
private let content: Content
init(parentViewController: UIViewController, @ViewBuilder content: () -> Content) {
self.parentViewController = parentViewController
self.content = content()
}
private init(parentViewController: UIViewController, content: Content) {
self.parentViewController = parentViewController
self.content = content
}
func makeContentView() -> any UIView & UIContentView {
let contentView = ContentView(configuration: self)
contentView.layoutMargins = .zero
return contentView
}
func updated(for state: any UIConfigurationState) -> SwiftUIHostingConfiguration {
guard let parentViewController else {
fatalError("Cannot update \(self) because parentViewController has been deallocated")
}
return SwiftUIHostingConfiguration(parentViewController: parentViewController, content: content)
}
}
private extension SwiftUIHostingConfiguration {
final class ContentView: UIView, UIContentView {
var configuration: UIContentConfiguration {
get {
currentConfiguration
}
set {
guard let newConfiguration = newValue as? SwiftUIHostingConfiguration<Content> else {
fatalError("Expected configuration to be of type \(SwiftUIHostingConfiguration<Content>.self)")
}
currentConfiguration = newConfiguration
hostingController.rootView = newConfiguration.content
// Removing and re-adding the hosting controller from the view hierarchy and then invalidating
// the intrinsic content size seems to be the only way to cause the UICollectionVIew and
// UITableView to relayout the cells and update the heights.
removeHostingControllerFromViewHierarchy()
addHostingControllerToViewHierarchy()
invalidateIntrinsicContentSize()
}
}
private let hostingController: UIHostingController<Content>
private var currentConfiguration: SwiftUIHostingConfiguration<Content>
init(configuration: SwiftUIHostingConfiguration<Content>) {
self.hostingController = UIHostingController(rootView: configuration.content)
self.currentConfiguration = configuration
super.init(frame: .zero)
addHostingControllerToViewHierarchy()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
removeHostingControllerFromViewHierarchy()
}
func supports(_ configuration: any UIContentConfiguration) -> Bool {
configuration is SwiftUIHostingConfiguration<Content>
}
}
}
private extension SwiftUIHostingConfiguration.ContentView {
private func addHostingControllerToViewHierarchy() {
guard let parentViewController = currentConfiguration.parentViewController else {
fatalError("Cannot setup \(Self.self) as parentViewController has been deallocated")
}
parentViewController.addChild(hostingController)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
addSubview(hostingController.view)
NSLayoutConstraint.activate([
hostingController.view.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
hostingController.view.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
])
hostingController.didMove(toParent: parentViewController)
}
private func removeHostingControllerFromViewHierarchy() {
hostingController.willMove(toParent: nil)
hostingController.view.removeFromSuperview()
hostingController.removeFromParent()
}
}
@Amzd
Copy link

Amzd commented Feb 7, 2026

Could you share your ExampleCellContentView? I am struggling with the cell sizes. How did you manage to get sizes based on the swiftui view content like in the video example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment