Working with Collection Views
1. Creating UICollectionView
| Property | Value | Description |
|---|---|---|
| UICollectionView | UICollectionView(frame: CGRect, collectionViewLayout: UICollectionViewLayout) |
Initializes a collection view with a specified frame and layout |
| dataSource | UICollectionViewDataSource |
Provides the data and views for the collection view |
| delegate | UICollectionViewDelegate |
Handles user interactions and layout customization |
Example: Basic UICollectionView Setup
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
collectionView.collectionViewLayout = layout
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Selected item at index path: \(indexPath)")
}
}
2. Implementing UICollectionViewDataSource
| Method | Description | Return Type | Notes |
|---|---|---|---|
| numberOfSections(in:) | Number of sections in the collection view | Int |
Required |
| collectionView(_:numberOfItemsInSection:) | Number of items in a section | Int |
Required |
| collectionView(_:cellForItemAt:) | Configure and return a cell for a specific item | UICollectionViewCell |
Required |
| collectionView(_:viewForSupplementaryElementOfKind:at:) | Return supplementary views (headers/footers) | UICollectionReusableView |
Optional |
Example: Basic UICollectionViewDataSource Implementation
class MyDataSource: NSObject, UICollectionViewDataSource {
let items = ["Item 1", "Item 2", "Item 3"]
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[indexPath.item]
return cell
}
}
Note: The
UICollectionViewDataSource protocol is required for any collection view to display content. It provides the data and cells needed to render items.3. Implementing UICollectionViewDelegate
| Method | Description | Return Type | Notes |
|---|---|---|---|
| collectionView(_:didSelectItemAt:) | Called when a cell is selected | Void |
Use to handle selection logic |
| collectionView(_:didDeselectItemAt:) | Called when a cell is deselected | Void |
Use to handle deselection logic |
| collectionView(_:willDisplay:forItemAt:) | Called just before a cell is displayed | Void |
Use for animations or updates |
| collectionView(_:didEndDisplaying:forItemAt:) | Called after a cell is removed from display | Void |
Use for cleanup or animation reset |
| collectionView(_:shouldHighlightItemAt:) | Controls whether a cell can be highlighted | Bool |
Return false to disable highlighting |
| collectionView(_:shouldSelectItemAt:) | Controls whether a cell can be selected | Bool |
Return false to disable selection |
| collectionView(_:shouldDeselectItemAt:) | Controls whether a cell can be deselected | Bool |
Return false to prevent deselection |
| collectionView(_:contextMenuConfigurationForItemAt:point:) | Provides context menu configuration for a cell | UIContextMenuConfiguration? |
Available from iOS 13+ |
Example: Handling Cell Selection
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let item = items[indexPath.item]
print("Selected item: \(item)")
}
Example: Customizing Highlight Behavior
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return indexPath.item != 0 // Prevent highlighting first item
}
4. Creating Custom Collection View Layouts
Example: Basic Custom Layout Class
import UIKit
class CustomCollectionViewLayout: UICollectionViewFlowLayout {
override func prepare() {
super.prepare()
itemSize = CGSize(width: 100, height: 100)
minimumInteritemSpacing = 10
minimumLineSpacing = 10
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
| Property | Description | Default Value |
|---|---|---|
| itemSize | Size of each collection view item | CGSize(width: 50, height: 50) |
| minimumInteritemSpacing | Minimum spacing between items in a row | 0 |
| minimumLineSpacing | Minimum spacing between lines | 0 |
| scrollDirection | Scroll direction of the collection view | .vertical |
| headerReferenceSize | Size of the header view | CGSize(width: 0, height: 0) |
| footerReferenceSize | Size of the footer view | CGSize(width: 0, height: 0) |
Note: Custom layouts should override
prepare to calculate layout attributes and shouldInvalidateLayout(forBoundsChange:) to determine when to recalculate.Warning: Layout calculations must be efficient to avoid performance issues during scrolling.
Key Points
- Custom layouts inherit from
UICollectionViewLayout - Override
prepareto calculate layout attributes - Use
shouldInvalidateLayout(forBoundsChange:)to control layout updates - Implement
layoutAttributesForElements(in:)to return layout attributes
5. Using UICollectionViewFlowLayout
| Property | Values | Description |
|---|---|---|
| itemSize | CGSize |
Size of each collection view item |
| minimumLineSpacing | CGFloat |
Minimum spacing between lines of items |
| minimumInteritemSpacing | CGFloat |
Minimum spacing between items in a line |
| sectionInset | UIEdgeInsets |
Insets for the section's content area |
| scrollDirection | UICollectionView.ScrollDirection |
Scroll direction of the collection view (horizontal or vertical) |
Example: Setting up UICollectionViewFlowLayout
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: 100, height: 100)
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 5
flowLayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
flowLayout.scrollDirection = .vertical
Note:
UICollectionViewFlowLayout is the default layout for collection views and is ideal for grid-like layouts.6. Configuring Item Size and Spacing
| Property | Values | Description |
|---|---|---|
| itemSize | CGSize |
Defines the size of each item in the collection view. |
| minimumLineSpacing | CGFloat |
Minimum spacing between lines of items. |
| minimumInteritemSpacing | CGFloat |
Minimum spacing between items in the same line. |
| sectionInset | UIEdgeInsets |
Insets for the entire section, applied to all items. |
Example: Setting Item Size and Spacing
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 5
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
collectionView.collectionViewLayout = layout
7. Implementing Section Insets
| Property | Value | Description |
|---|---|---|
| sectionInset | UIEdgeInsets |
Insets for the entire section content area |
| minimumLineSpacing | CGFloat |
Minimum spacing between lines in the collection view |
| minimumInteritemSpacing | CGFloat |
Minimum spacing between items in the same line |
Example: Setting Section Insets
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 5
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
Note:
sectionInset defines the space around the content of a section, including headers and footers.8. Using Supplementary Views
Supplementary views in iOS Collection Views are used to display additional content that is associated with a section or item, such as headers or footers.
| View Type | Description | Usage |
|---|---|---|
| UICollectionReusableView | Base class for supplementary views | Subclass to create custom supplementary views |
| UICollectionViewElementKindSectionHeader | Identifier for section header views | Used in dequeueReusableSupplementaryView |
| UICollectionViewElementKindSectionFooter | Identifier for section footer views | Used in dequeueReusableSupplementaryView |
Example: Registering and Using a Header View
class HeaderView: UICollectionReusableView {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
label.text = "Section Header"
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
}
// Register the header view
collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
// Dequeue the header view in your data source
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, withReuseIdentifier: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView
return header
}
Note: Supplementary views must be registered before they can be dequeued. The
kind parameter determines whether it's a header or footer.Warning: Make sure to use unique reuse identifiers for different supplementary view types to prevent incorrect views from being reused.
9. Implementing Cell Selection
| Property | Value | Description |
|---|---|---|
| allowsSelection | true |
Enables or disables selection in the collection view |
| allowsMultipleSelection | false |
Allows multiple cells to be selected at once |
| indexPathForSelectedItems | IndexPath? |
Returns the index paths of selected items |
Example: Selecting a Cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Selected item at path: \(indexPath)")
}
Example: Deselecting a Cell
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("Deselected item at path: \(indexPath)")
}
Note: To enable selection, set
allowsSelection to true.
Warning: Ensure that
didSelectItemAt and didDeselectItemAt are implemented to handle selection changes.
10. Using Compositional Layout
| Property | Description | Values |
|---|---|---|
| sectionInset | Insets for the section content | UIEdgeInsets |
| minimumLineSpacing | Minimum spacing between lines | CGFloat |
| minimumInteritemSpacing | Minimum spacing between items | CGFloat |
Example: Creating a Compositional Layout
let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(50)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(50)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitems: [item]
)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10)
return section
}
Note: Compositional layouts provide a more flexible and powerful way to define collection view layouts compared to the older
UICollectionViewFlowLayout.
Warning: Ensure that the
UICollectionViewCompositionalLayout is properly configured to avoid layout issues or performance problems.
11. Implementing Diffable Data Source
Example: Setting up Diffable Data Source
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
override func viewDidLoad() {
super.viewDidLoad()
setupDataSource()
}
private func setupDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
// Configure cell with item
return cell
}
}
private func updateUI() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems([Item(title: "First Item")], toSection: .main)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
| Property | Description | Notes |
|---|---|---|
| UICollectionViewDiffableDataSource | Manages collection view data with diffable updates | Replaces older UITableViewDataSource approach |
| NSDiffableDataSourceSnapshot | Represents current state of data in collection view | Used to apply updates via apply() |
| apply(snapshot:animatingDifferences:) | Applies snapshot to collection view with animation | Supports animating changes between snapshots |
Note:
UICollectionViewDiffableDataSource requires iOS 13.0+ and is not available in earlier versions.Warning: When using
apply(), ensure that all items and sections are properly registered with the collection view.