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, към който трябва да се добавят всички subview — не ги добавяйте директно към клетката, защото contentView отговаря за правилната анимация на избор и редактиране.

FlowLayout срещу 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 — никога не добавяйте subview директно към 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 constraints
        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 грешки; (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 срещу UITableView: кога какво да изберем

UITableView и UICollectionView — два основни компонента на UIKit за показване на списъци. UITableView е по-прост и предназначен за едноколонни списъци. UICollectionView е по-гъвкав и поддържа всякакви оформления. Изборът между тях зависи от визуалната структура на данните и необходимата персонализация.

ПараметърUITableViewUICollectionView
ОформлениеСамо вертикален списъкСписък, мрежа, каскада, въртележка, персонализирано
СложностНиска (прост DataSource)Средна (Layout + DataSource + Delegate)
Клетка по подразбиране4 стила (basic, subtitle, value1, value2)Само празна (персонализирана чрез contentView)
Режим на редактиранеВграден (delete, move, insert)Изисква ръчна имплементация
Header/FooterВградени (viewForHeaderInSection)Чрез допълнителни изгледи
ПроизводителностВисока за списъциВисока за всякакви оформления
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) клетката е dequeued с правилния 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 срещу 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 за всички subview — не ги добавяйте директно към клетката.
  • UICollectionViewDiffableDataSource — модерен типобезопасен DataSource с автоматична анимация на промени.
  • Персонализиран UICollectionViewLayout позволява създаване на произволни оформления (кръг, спирала, диаграма).
  • 72% от iOS приложенията в топ 100 на App Store използват UICollectionView за показване на съдържание.
  • За прости списъци използвайте UITableView; за всичко останало — UICollectionView.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също