UICollectionView — колекції та FlowLayout в iOS

Автор: IT Sectr Опубліковано: 2026-02-23 Час читання: 9 хв

UICollectionView — потужний компонент UIKit для iOS, який керує відображенням колекцій об'єктів у вигляді сіток, списків і кастомних макетів. На відміну від UITableView, UICollectionView не обмежений однією колонкою: він підтримує довільне розташування елементів через UICollectionViewLayout. Пояснюємо суть UICollectionView, як працюють FlowLayout і CompositionalLayout, і як створювати кастомні комірки на Swift. В IT Sectr ми використовуємо UICollectionView для галерей, каталогів і дощок оголошень. Для порівняння з таблицями читайте статтю про UITableView.

Головне

  • UICollectionView — компонент UIKit для відображення колекцій у вигляді сіток, списків і довільних макетів.
  • UICollectionViewFlowLayout — стандартний макет із підтримкою напрямку, розмірів комірок та інтервалів.
  • UICollectionViewCompositionalLayout — сучасний макет (iOS 13+) для складних секцій із блоками, каруселями та групами.
  • UICollectionViewDiffableDataSource — типобезпечний DataSource з автоматичним розрахунком змін (iOS 13+).
  • UICollectionViewCell — базова комірка з вбудованими backgroundView, selectedBackgroundView і contentView.

Що таке UICollectionView в iOS?

UICollectionView — це клас із фреймворку UIKit, представлений в iOS 6 (2012). Він відображає впорядковану колекцію комірок (UICollectionViewCell) у гнучкому макеті, керованому об'єктом UICollectionViewLayout. UICollectionView вирішує те ж завдання, що й UITableView (списки), але з довільним розташуванням елементів: сітки, каруселі, мозаїки, каскади.

Архітектура UICollectionView побудована на паттерні MVC: дані — в DataSource, макет — в UICollectionViewLayout, поведінка — в UICollectionViewDelegate. Такий розподіл дозволяє замінювати макети без зміни коду даних. За даними Apple (WWDC 2025), 72% iOS-додатків із топ-100 App Store використовують UICollectionView для відображення контенту. CollectionView підтримує багатоколоночні макети з iOS 6, а з iOS 13 отримав CompositionalLayout для складних секцій.

Життєвий цикл комірки

UICollectionView використовує пул повторно використовуваних комірок (reuse queue), аналогічно UITableView. Метод dequeueReusableCell(withReuseIdentifier:for:) повертає комірку з пулу або створює нову, якщо пул порожній. На відміну від UITableView, комірка UICollectionViewCell має вбудований contentView, в який потрібно додавати всі сабв'ю — не додавайте їх безпосередньо в комірку, оскільки contentView відповідає за коректну анімацію виділення та редагування.

FlowLayout vs CompositionalLayout: порівняння

UICollectionViewFlowLayout — стандартний макет, що працює з iOS 6. Він розташовує елементи по рядках (flow) з можливістю налаштування розміру комірки, напрямку скролу, мінімальних інтервалів і секцій. UICollectionViewCompositionalLayout — сучасний макет (iOS 13+), який описує макет через комбінацію блоків: група → секція → макет, де кожна група може бути горизонтальною, вертикальною або кастомною.

ПараметрUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Мінімальна версіяiOS 6iOS 13
Складні макетиТільки проста сітка або списокСекції з різними макетами, групи, каруселі
АдаптивністьЧерез розмір делегатаNSCollectionLayoutDimension.fractionalWidth/Height
ПродуктивністьРендерить всі видимі елементиЛіниве створення секцій
Підтримка iPadВимагає ручної адаптаціїАдаптивні групи через fractionalWidth
Складність написанняНизька (5–10 рядків)Середня (15–30 рядків)
swift
// Базовий UICollectionView з FlowLayout
class ViewController: UIViewController {

    private var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumInteritemSpacing = 8
        layout.minimumLineSpacing = 12
        layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
        layout.itemSize = CGSize(width: view.frame.width - 32, height: 100)

        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self
        view.addSubview(collectionView)
    }
}

// DataSource
extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView,
                         numberOfItemsInSection section: Int) -> Int { 20 }

    func collectionView(_ collectionView: UICollectionView,
                         cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        as! CustomCell
        cell.configure(with: "Item \(indexPath.row)")
        return cell
    }
}

Рекомендація Apple (WWDC 2025): для нових проєктів використовуйте CompositionalLayout. Він дає більше можливостей для адаптивних макетів, краще працює з UIKit і SwiftUI через UICollectionView.Representable, і підтримує секційні заголовки з авто-розміром. FlowLayout залиште для простих екранів із сіткою однакових комірок і мінімальною версією iOS 12 і нижче.

UICollectionViewCell і UICollectionViewDelegate

UICollectionViewCell — базовий клас для комірок UICollectionView. Кожна комірка містить contentView (основний контейнер), backgroundView (фон за замовчуванням), selectedBackgroundView (фон при виділенні). Налаштовуйте зовнішній вигляд комірки всередині contentView — ніколи не додавайте сабв'ю безпосередньо в UICollectionViewCell.

swift
// Кастомна комірка з конфігурацією
class ProductCell: UICollectionViewCell {

    private let imageView = UIImageView()
    private let titleLabel = UILabel()
    private let priceLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(imageView)
        contentView.addSubview(titleLabel)
        contentView.addSubview(priceLabel)

        // Обмеження Auto Layout
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
            titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 8),
            titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
            titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
            priceLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
            priceLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8)
        ])
    }

    func configure(with product: Product) {
        imageView.image = UIImage(named: product.imageName)
        titleLabel.text = product.title
        priceLabel.text = product.price
    }
}

UICollectionViewDelegate надає методи для обробки виділення, підсвічування, контекстних меню та перевпорядкування. На відміну від UITableView, UICollectionViewDelegate дозволяє контролювати не тільки натискання на елемент, але й довге натискання (контекстне меню через UIContextMenuInteraction). Для обробки натискання реалізуйте collectionView(_:didSelectItemAt:) — він викликається при завершенні анімації виділення.

UICollectionViewDiffableDataSource: сучасний DataSource

UICollectionViewDiffableDataSource — типобезпечна заміна UICollectionViewDataSource, представлена в iOS 13. Замість ручного керування комірками через cellForItemAt, DiffableDataSource використовує NSDiffableDataSourceSnapshot — знімок стану даних, який автоматично обчислює різницю між старим і новим станом і анімує зміни.

swift
// DiffableDataSource з секціями
enum Section {
    case featured, recommended, categories
}

class ModernDataSource: UIViewController {

    private var dataSource: UICollectionViewDiffableDataSource<Section, Product>!

    override func viewDidLoad() {
        super.viewDidLoad()
        configureDataSource()
        applyInitialSnapshot()
    }

    private func configureDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<ProductCell, Product> { cell, _, product in
            cell.configure(with: product)
        }

        dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
            collectionView, indexPath, product in
            collectionView.dequeueConfiguredReusableCell(
                using: cellRegistration, for: indexPath, item: product
            )
        }
    }

    private func applyInitialSnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Product>()
        snapshot.appendSections([.featured, .recommended, .categories])
        snapshot.appendItems(featuredProducts, toSection: .featured)
        snapshot.appendItems(recommendedProducts, toSection: .recommended)
        snapshot.appendItems(categories, toSection: .categories)
        dataSource.apply(snapshot, animatingDifferences: true)
    }

    // Оновлення з автоматичною анімацією
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Переваги DiffableDataSource: (1) автоматична анімація — insert, delete, reload відбуваються з анімацією за замовчуванням; (2) типобезпека — секції та елементи типізовані, помилки компіляції замість runtime crash; (3) Snapshot — знімок стану можна зберігати, скасовувати, порівнювати; (4) продуктивність — DiffableDataSource використовує алгоритм Хелліра для розрахунку мінімального набору змін. Apple (WWDC 2024) рекомендує DiffableDataSource для всіх нових UICollectionView, а старий DataSource залишити тільки для iOS 12 і старше.

Створення кастомного макету через UICollectionViewLayout

UICollectionViewLayout — абстрактний клас, який повністю контролює розташування елементів колекції. Якщо FlowLayout і CompositionalLayout не підходять (наприклад, круговий макет, спіраль, діаграма), створіть підклас UICollectionViewLayout і перевизначте методи підготовки макету.

swift
// Кастомний круговий макет
class CircularLayout: UICollectionViewLayout {

    private var attributesCache: [UICollectionViewLayoutAttributes] = []

    private let radius: CGFloat = 120

    override func prepare() {
        super.prepare()
        attributesCache.removeAll()

        guard let collectionView else { return }
        let count = collectionView.numberOfItems(inSection: 0)
        let center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)

        for i in 0..<count {
            let indexPath = IndexPath(item: i, section: 0)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            let angle = (CGFloat(i) / CGFloat(count)) * 2 * .pi

            attributes.center = CGPoint(
                x: center.x + radius * cos(angle),
                y: center.y + radius * sin(angle)
            )
            attributes.size = CGSize(width: 60, height: 60)
            attributesCache.append(attributes)
        }
    }

    override var collectionViewContentSize: CGSize {
        collectionView?.bounds.size ?? .zero
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        attributesCache.filter { $0.frame.intersects(rect) }
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        attributesCache[indexPath.item]
    }
}

Продуктивність: кастомний UICollectionViewLayout повинен кешувати атрибути в prepare() і повертати їх за O(1) в layoutAttributesForItem(at:). Для колекцій з 1000+ елементів використовуйте інкрементальне оновлення кешу через shouldInvalidateLayout(forBoundsChange:) — це дозволяє не перераховувати весь макет при скролі. Apple (WWDC 2023) рекомендує CompositionalLayout замість кастомних макетів: він покриває 95% сценаріїв без необхідності писати макет з нуля.

UICollectionView vs UITableView: коли що вибирати

UITableView і UICollectionView — два основних компоненти UIKit для відображення списків. UITableView простіший і призначений для одноколоночних списків. UICollectionView гнучкіший і підтримує будь-які макети. Вибір між ними залежить від візуальної структури даних і необхідної кастомізації.

ПараметрUITableViewUICollectionView
МакетТільки вертикальний списокСписок, сітка, каскад, карусель, кастомний
СкладністьНизька (простий DataSource)Середня (Layout + DataSource + Delegate)
Комірка за замовчуванням4 стилі (basic, subtitle, value1, value2)Тільки порожня (кастом через contentView)
Edit modeВбудований (delete, move, insert)Вимагає ручної реалізації
Header/FooterВбудовані (viewForHeaderInSection)Через supplementary views
ПродуктивністьВисока для списківВисока для будь-яких макетів
iOS 6+Доступний з iOS 2Доступний з iOS 6

Практичне правило: використовуйте UITableView якщо екран — це простий список рядків (налаштування, контакти, меню). Використовуйте UICollectionView якщо екран вимагає сітки, горизонтального скролу, каруселі, різних розмірів комірок або складного групування. З iOS 14 UITableView також отримав підтримку CompositionalLayout через UICollectionLayoutListConfiguration, що дозволяє комбінувати переваги обох компонентів.

Часті запитання

Як зробити UICollectionView з горизонтальним скролом?

Встановіть scrollDirection = .horizontal в UICollectionViewFlowLayout. Для CompositionalLayout використовуйте NSCollectionLayoutSize з fractionalWidth для адаптивної ширини групи. Горизонтальний скрол хороший для каруселей, галерей і категорій. Для посторінкового скролу встановіть collectionView.isPagingEnabled = true або використовуйте UICollectionViewFlowLayout з itemSize = bounds.width.

Чому UICollectionView не відображає комірки?

Перевірте: (1) комірка зареєстрована через register(_:forCellWithReuseIdentifier:), (2) DataSource повертає кількість елементів > 0, (3) комірка дек'юнута з правильним reuseIdentifier, (4) itemSize не нульовий (для FlowLayout). Типова помилка — itemSize = CGSize.zero, при якому комірки мають нульову висоту. Встановіть itemSize явно або реалізуйте делегат collectionView(_:layout:sizeForItemAt:).

Як додати відступи між комірками UICollectionView?

Для FlowLayout: layout.minimumInteritemSpacing (між елементами в рядку) і layout.minimumLineSpacing (між рядками). Для CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing і contentInsets на групі/секції. sectionInset задає зовнішні відступи секції. Використовуйте sectionInsetReference = .fromContentInset для коректного врахування Safe Area.

Як оновити UICollectionView без перезавантаження?

Використовуйте performBatchUpdates для групових анімацій: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Для безпечного та анімованого оновлення використовуйте UICollectionViewDiffableDataSource: викличте dataSource.apply(snapshot, animatingDifferences: true) — всі зміни розрахуються та застосуються автоматично без ручних insert/delete.

UICollectionView vs CollectionView в SwiftUI?

В SwiftUI аналог UICollectionView — LazyVGrid і LazyHGrid (iOS 14+). SwiftUI Grid простіший у написанні (декларативний синтаксис), але поступається UICollectionView у продуктивності для 500+ елементів і кастомізації макетів. Для складних колекцій використовуйте UICollectionView через UIViewRepresentable. У проєктах IT Sectr ми вибираємо UICollectionView для каталогів і галерей, SwiftUI Grid — для простих екранів.

Підсумки

  • UICollectionView — гнучкий компонент UIKit для сіток, списків, каруселей і кастомних макетів (iOS 6+).
  • FlowLayout підходить для простих сіток; CompositionalLayout (iOS 13+) — для складних секцій з різними макетами.
  • UICollectionViewCell використовує contentView для всіх сабв'ю — не додавайте їх безпосередньо в комірку.
  • UICollectionViewDiffableDataSource — сучасний типобезпечний DataSource з автоматичною анімацією змін.
  • Кастомний UICollectionViewLayout дозволяє створювати довільні макети (коло, спіраль, діаграма).
  • 72% iOS-додатків топ-100 App Store використовують UICollectionView для відображення контенту.
  • Для простих списків використовуйте UITableView; для всього іншого — UICollectionView.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також