For quite some time, the traditional way to implement cell views for tables and collections in UIKit was to subclass UITableViewCell or UICollectionViewCell. However, starting with iOS 14, Apple introduced UIContentView and UIContentConfiguration, which allow developers to define custom content views for cells without subclassing. In practice, this turns out to be a very effective approach to implementing custom views for cells, and enables high interchangeability and reusability of these views.
In this article I’ll guide you through the process of creating custom cell views with the UIContentView and configuring them with the UIContentConfiguration.

Want to see this code in action?
I created a sample Xcode project on Gihub that demonstrates the implementation discussed in this article. Feel free to check it out and use in your projects.
What is Content View?
Table and collection views display data in cells. The UI elements of a cell are placed inside what is referred to as a content view. The content view of a cell in UIKit consists of two parts:
- View—The definition of the content view as a subclass of the
UIViewthat conforms toUIContentViewprotocol. - Configuration—The object that conforms to the
UIContentConfigurationprotocol and defines the content and settings of the view.
The view handles the creation of UI elements and their layout, while the configuration provides the content and styling for the view. The content view applies the configuration on the first load, and then whenever the data changes.
I established the following workflow for creating custom content views and their configurations:
- Define the view
- Conform to UIContentView protocol
- Implement simple initializer for the view
- Define content view configuration
- Apply configuration to the view
Let’s take a closer look at each of these steps.
UIContentView to populate the UICollectionViewController with color picker, text field, and delete button. You literally can plug in any view you want into your table/collection view using UIContentView protocol.I. Define Content View
First, consider what UI elements will constitute your view, such as labels, images, controls, etc. Start by creating a UIView subclass and declare your UI elements as its properties.
class CustomContentView: UIView {
let label = UILabel()
let imageView = UIImageView()
}
II. Conform to UIContentView Protocol
To make your custom content view fully functional within a table or collection view cell, it needs to conform to the UIContentView protocol. Adopt the UIContentView protocol by listing it after the UIView class.
class CustomContentView: UIView, UIContentView
To conform the view to UIContentView protocol declare a configuration variable property of type any UIContentConfiguration.
var configuration: any UIContentConfiguration
The keyword any in this case means that theoretically you could pass any configuration to create your custom content view, but in practice for every custom view there’s a dedicated configuration object.
III. Implement Simple Initializer
To provide initial configuration implement a simple initializer that takes an argument of the any UIContentConfiguration type. Make sure to call the dedicated init(frame:) initializer on the UIView super class. It’s fine to pass in the CGSize.zero in the super class initializer, because the actual size of the content view is set intrinsically by its subviews.
init(configuration: any UIContentConfiguration) {
self.configuration = configuration
super.init(frame: .zero)
layOutViews()
}
The initializer is also a great place to lay out the UI elements within the content view. Since we’re subclassing the UIView here, we can take advantage of the direct access to its properties and methods for laying out the subviews. It’s very important to set proper constraints for the subviews of your content view, as these constraints are the key to ensure that UIKit is able to calculate the intrinsic size of the content view.
func layOutViews() {
let padding: CGFloat = 16.0
let stackView = UIStackView(arrangedSubviews: [imageView, label])
stackView.axis = .horizontal
stackView.spacing = padding / 2.0
addSubview(stackView) // Using UIView methods directly
stackView.translatesAutoresizingMaskIntoConstraints = false
// Referring to UIView anchors directly
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor, constant: padding),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -padding),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding)
])
}
The UIView also requires the implementation of the init(coder:) initializer, however its implementation is irrelevant for the purposes of this article.
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
IV. Define Content View Configuration
In context of custom content views in UIKit, the idea behind configuration is to keep the view and model data independent from each other. The configuration is essentially a helper structure that provides the content and updates the view due to changes in the data.
To create a configuration for content view, create a structure that conforms to UIContentConfiguration protocol, and declare in it the properties that provide content for the view. The UIContentConfiguration protocol requires the implementation of two methods:
makeContentView()— This method is called whenever the cell that contains the content view needs to appear on the screen. You can use this method to perform any checks or initial setup that you need to return the relevant view.updated(for:)— The system calls this method when the state of the cell changes, such as when the cell is selected, disabled, etc. This method can be used to check the state that’s being passed by the system and return the configuration adjusted according to your needs.
extension CustomContentView {
struct Configuration: UIContentConfiguration {
var image: UIImage?
var text: String?
func makeContentView() -> any UIView & UIContentView {
return CustomContentView(configuration: self)
}
func updated(for state: any UIConfigurationState) -> CustomContentView.Configuration {
return self
}
}
}
This is the simplest implementation of the UIContentConfiguration object.
V. Apply Configuration to Content View
Once you have your view and configuration ingredients ready, it’s time to smash them together into one tasty content view sandwich. Simply apply your configuration whenever it’s being set with the help of didSet observer on the configuration property defined earlier in Step II.
var configuration: any UIContentConfiguration {
didSet {
configure(configuration)
}
}
func configure(_ configuration: any UIContentConfiguration) {
// 1. Check if the configuration type matches
guard let configuration = configuration as? Configuration else { return }
// 2. Apply your configuration to the views
label.text = configuration.text
imageView.image = configuration.image
}
What’s Next
At this point, the content view is ready to be used in table or collection view. To use content view in a cell simply create an instance of your configuration, fill the relevant configuration properties with your data, and assign your configuration to the contentConfiguration property of the cell wherever you handle the cell creation for your table/collection view.
// 1. Create instance of your configuration
var contentConfiguration = CustomContentView.Configuration()
// 2. Set relevant data
contentConfiguration.text = item.title
contentConfiguration.image = item.image
// 3. Set contentConfiguration of the cell to your configuration
cell.contentConfiguration = contentConfiguration
This code will work with any cell—be it UITableViewCell, UICollectionViewCell, so you can paste it wherever you need to configure your cells.
Tip: Extend Your Cell Types
You can streamline the process of initializing the right configuration by extending the cell view type your working with. Simple declare a function that returns the configuration you need.
extension UICollectionViewCell {
func customContentViewConfiguration() -> CustomContentView.Configuration {
return CustomContentView.Configuration()
}
}
Using this extension method is as simple as calling it on your cell.
let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
var contentConfiguration = cell.customContentViewConfiguration()
Wrap Up
This article scratches only the surface of what can be achieved with UIContentView, but it’s enough to get you started. If done right, the same content views can be used for any table or collection view. I encourage you to check out my sample Xcode project that I created for this article to explore more use cases for the content views, such as creating form views and custom collection view layouts. Happy viewing!

