UICollectionView — istota, kolekcje i FlowLayout w iOS

Autor: IT Sectr Opublikowano: 2026-02-23 Czas czytania: 9 min

UICollectionView — potężny komponent UIKit dla iOS, zarządzający wyświetlaniem kolekcji obiektów w postaci siatek, list i niestandardowych układów. W przeciwieństwie do UITableView, UICollectionView nie jest ograniczony do jednej kolumny: obsługuje dowolne rozmieszczenie elementów poprzez UICollectionViewLayout. Opisujemy istotę UICollectionView, jak działają FlowLayout i CompositionalLayout, oraz jak tworzyć niestandardowe komórki w Swift. W IT Sectr używamy UICollectionView do galerii, katalogów i tablic ogłoszeń. Dla porównania z tabelami przeczytaj artykuł o UITableView.

Najważniejsze

  • UICollectionView — komponent UIKit do wyświetlania kolekcji w postaci siatek, list i dowolnych układów.
  • UICollectionViewFlowLayout — standardowy układ z obsługą kierunku, rozmiarów komórek i odstępów.
  • UICollectionViewCompositionalLayout — nowoczesny układ (iOS 13+) do złożonych sekcji z blokami, karuzelami i grupami.
  • UICollectionViewDiffableDataSource — typowo bezpieczne źródło danych z automatycznym obliczaniem zmian (iOS 13+).
  • UICollectionViewCell — podstawowa komórka z wbudowanymi backgroundView, selectedBackgroundView i contentView.

Czym jest UICollectionView w iOS?

UICollectionView — to klasa z frameworka UIKit, wprowadzona w iOS 6 (2012). Wyświetla uporządkowaną kolekcję komórek (UICollectionViewCell) w elastycznym układzie zarządzanym przez obiekt UICollectionViewLayout. UICollectionView rozwiązuje to samo zadanie co UITableView (listy), ale z dowolnym rozmieszczeniem elementów: siatki, karuzele, mozaiki, kaskady.

Architektura UICollectionView jest zbudowana na wzorcu MVC: dane — w DataSource, układ — w UICollectionViewLayout, zachowanie — w UICollectionViewDelegate. Taki podział pozwala na wymianę układów bez zmiany kodu danych. Według Apple (WWDC 2025), 72% aplikacji iOS z top 100 App Store używa UICollectionView do wyświetlania treści. CollectionView obsługuje wielokolumnowe układy od iOS 6, a od iOS 13 otrzymał CompositionalLayout dla złożonych sekcji.

Cykl życia komórki

UICollectionView używa puli ponownie wykorzystywanych komórek (reuse queue), podobnie jak UITableView. Metoda dequeueReusableCell(withReuseIdentifier:for:) zwraca komórkę z puli lub tworzy nową, jeśli pula jest pusta. W przeciwieństwie do UITableView, komórka UICollectionViewCell ma wbudowany contentView, do którego należy dodawać wszystkie subview — nie dodawaj ich bezpośrednio do komórki, ponieważ contentView odpowiada za poprawną animację zaznaczenia i edycji.

FlowLayout vs CompositionalLayout: porównanie

UICollectionViewFlowLayout — standardowy układ działający od iOS 6. Rozmieszcza elementy w wierszach (flow) z możliwością ustawienia rozmiaru komórki, kierunku przewijania, minimalnych odstępów i sekcji. UICollectionViewCompositionalLayout — nowoczesny układ (iOS 13+), który opisuje układ przez kombinację bloków: grupa → sekcja → układ, gdzie każda grupa może być pozioma, pionowa lub niestandardowa.

ParametrUICollectionViewFlowLayoutUICollectionViewCompositionalLayout
Minimalna wersjaiOS 6iOS 13
Złożone układyTylko prosta siatka lub listaSekcje z różnymi układami, grupy, karuzele
AdaptacyjnośćPrzez rozmiar delegataNSCollectionLayoutDimension.fractionalWidth/Height
WydajnośćRenderuje wszystkie widoczne elementyLeniwie tworzy sekcje
Wsparcie iPadWymaga ręcznej adaptacjiGrupy adaptacyjne przez fractionalWidth
Trudność pisaniaNiska (5–10 linii)Średnia (15–30 linii)
swift
// Podstawowy UICollectionView z 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
    }
}

Zalecenie Apple (WWDC 2025): dla nowych projektów używaj CompositionalLayout. Daje więcej możliwości dla adaptacyjnych układów, lepiej współpracuje z UIKit i SwiftUI przez UICollectionView.Representable, oraz obsługuje nagłówki sekcji z auto-rozmiarem. FlowLayout pozostaw dla prostych ekranów z siatką identycznych komórek i minimalną wersją iOS 12 i niższą.

UICollectionViewCell i UICollectionViewDelegate

UICollectionViewCell — podstawowa klasa dla komórek UICollectionView. Każda komórka zawiera contentView (główny kontener), backgroundView (tło domyślne), selectedBackgroundView (tło przy zaznaczeniu). Dostosowuj wygląd komórki wewnątrz contentView — nigdy nie dodawaj subview bezpośrednio do UICollectionViewCell.

swift
// Niestandardowa komórka z konfiguracją
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 udostępnia metody do obsługi zaznaczenia, podświetlenia, menu kontekstowych i zmiany kolejności. W przeciwieństwie do UITableView, UICollectionViewDelegate pozwala kontrolować nie tylko kliknięcie elementu, ale także długie przytrzymanie (menu kontekstowe przez UIContextMenuInteraction). Do obsługi kliknięcia zaimplementuj collectionView(_:didSelectItemAt:) — jest wywoływane po zakończeniu animacji zaznaczenia.

UICollectionViewDiffableDataSource: nowoczesne źródło danych

UICollectionViewDiffableDataSource — typowo bezpieczna zamiana UICollectionViewDataSource, wprowadzona w iOS 13. Zamiast ręcznego zarządzania komórkami przez cellForItemAt, DiffableDataSource używa NSDiffableDataSourceSnapshot — migawki stanu danych, która automatycznie oblicza różnicę między starym a nowym stanem i animuje zmiany.

swift
// DiffableDataSource z sekcjami
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)
    }

    // Aktualizacja z automatyczną animacją
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Zalety DiffableDataSource: (1) automatyczna animacja — insert, delete, reload odbywają się z domyślną animacją; (2) typowe bezpieczeństwo — sekcje i elementy są typowane, błędy kompilacji zamiast błędów runtime; (3) Snapshot — migawkę stanu można zapisywać, cofać, porównywać; (4) wydajność — DiffableDataSource używa algorytmu Hellira do obliczania minimalnego zestawu zmian. Apple (WWDC 2024) zaleca DiffableDataSource dla wszystkich nowych UICollectionView, a stare DataSource pozostawić tylko dla iOS 12 i starszych.

Tworzenie niestandardowego układu przez UICollectionViewLayout

UICollectionViewLayout — abstrakcyjna klasa, która całkowicie kontroluje rozmieszczenie elementów kolekcji. Jeśli FlowLayout i CompositionalLayout nie pasują (na przykład układ kołowy, spirala, diagram), utwórz podklasę UICollectionViewLayout i nadpisz metody przygotowania układu.

swift
// Niestandardowy układ kołowy
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]
    }
}

Wydajność: niestandardowy UICollectionViewLayout powinien buforować atrybuty w prepare() i zwracać je w O(1) w layoutAttributesForItem(at:). Dla kolekcji z 1000+ elementami używaj przyrostowej aktualizacji pamięci podręcznej przez shouldInvalidateLayout(forBoundsChange:) — pozwala to nie przeliczać całego układu podczas przewijania. Apple (WWDC 2023) zaleca CompositionalLayout zamiast niestandardowych układów: pokrywa 95% scenariuszy bez potrzeby pisania układu od zera.

UICollectionView vs UITableView: kiedy co wybrać

UITableView i UICollectionView — dwa główne komponenty UIKit do wyświetlania list. UITableView jest prostszy i przeznaczony do jednokolumnowych list. UICollectionView jest bardziej elastyczny i obsługuje dowolne układy. Wybór między nimi zależy od wizualnej struktury danych i wymaganej personalizacji.

ParametrUITableViewUICollectionView
UkładTylko pionowa listaLista, siatka, kaskada, karuzela, niestandardowy
ZłożonośćNiska (proste DataSource)Średnia (Layout + DataSource + Delegate)
Domyślna komórka4 style (basic, subtitle, value1, value2)Tylko pusta (niestandardowa przez contentView)
Tryb edycjiWbudowany (delete, move, insert)Wymaga ręcznej implementacji
Header/FooterWbudowane (viewForHeaderInSection)Przez widoki uzupełniające
WydajnośćWysoka dla listWysoka dla dowolnych układów
iOS 6+Dostępny od iOS 2Dostępny od iOS 6

Praktyczna zasada: używaj UITableView jeśli ekran to prosta lista wierszy (ustawienia, kontakty, menu). Używaj UICollectionView jeśli ekran wymaga siatki, poziomego przewijania, karuzeli, różnych rozmiarów komórek lub złożonego grupowania. Od iOS 14 UITableView również otrzymał wsparcie CompositionalLayout przez UICollectionLayoutListConfiguration, co pozwala łączyć zalety obu komponentów.

Często zadawane pytania

Jak zrobić UICollectionView z poziomym przewijaniem?

Ustaw scrollDirection = .horizontal w UICollectionViewFlowLayout. Dla CompositionalLayout użyj NSCollectionLayoutSize z fractionalWidth dla adaptacyjnej szerokości grupy. Poziome przewijanie jest dobre dla karuzel, galerii i kategorii. Dla przewijania stronicowego ustaw collectionView.isPagingEnabled = true lub użyj UICollectionViewFlowLayout z itemSize = bounds.width.

Dlaczego UICollectionView nie wyświetla komórek?

Sprawdź: (1) komórka jest zarejestrowana przez register(_:forCellWithReuseIdentifier:), (2) DataSource zwraca liczbę elementów > 0, (3) komórka jest dequeued z poprawnym reuseIdentifier, (4) itemSize nie jest zerowy (dla FlowLayout). Typowy błąd — itemSize = CGSize.zero, przy którym komórki mają zerową wysokość. Ustaw itemSize jawnie lub zaimplementuj delegata collectionView(_:layout:sizeForItemAt:).

Jak dodać odstępy między komórkami UICollectionView?

Dla FlowLayout: layout.minimumInteritemSpacing (między elementami w wierszu) i layout.minimumLineSpacing (między wierszami). Dla CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing i contentInsets na grupie/sekcji. sectionInset określa zewnętrzne odstępy sekcji. Użyj sectionInsetReference = .fromContentInset dla poprawnego uwzględnienia Safe Area.

Jak zaktualizować UICollectionView bez przeładowania?

Użyj performBatchUpdates dla animacji grupowych: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Dla bezpiecznej i animowanej aktualizacji użyj UICollectionViewDiffableDataSource: wywołaj dataSource.apply(snapshot, animatingDifferences: true) — wszystkie zmiany zostaną obliczone i zastosowane automatycznie bez ręcznych insert/delete.

UICollectionView vs CollectionView w SwiftUI?

W SwiftUI odpowiednik UICollectionView to LazyVGrid i LazyHGrid (iOS 14+). SwiftUI Grid jest prostszy w pisaniu (deklaratywna składnia), ale ustępuje UICollectionView pod względem wydajności dla 500+ elementów i personalizacji układów. Dla złożonych kolekcji używaj UICollectionView przez UIViewRepresentable. W projektach IT Sectr wybieramy UICollectionView dla katalogów i galerii, SwiftUI Grid — dla prostych ekranów.

Podsumowanie

  • UICollectionView — elastyczny komponent UIKit dla siatek, list, karuzel i niestandardowych układów (iOS 6+).
  • FlowLayout nadaje się do prostych siatek; CompositionalLayout (iOS 13+) — do złożonych sekcji z różnymi układami.
  • UICollectionViewCell używa contentView dla wszystkich subview — nie dodawaj ich bezpośrednio do komórki.
  • UICollectionViewDiffableDataSource — nowoczesne, typowo bezpieczne źródło danych z automatyczną animacją zmian.
  • Niestandardowy UICollectionViewLayout pozwala tworzyć dowolne układy (koło, spirala, diagram).
  • 72% aplikacji iOS z top 100 App Store używa UICollectionView do wyświetlania treści.
  • Dla prostych list używaj UITableView; dla wszystkiego innego — UICollectionView.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również