UICollectionView — en kraftfull UIKit-komponent för iOS som hanterar visning av objektsamlingar i form av rutnät, listor och anpassade layouter. Till skillnad från UITableView är UICollectionView inte begränsad till en kolumn: den stöder godtycklig placering av element via UICollectionViewLayout. Vi förklarar essensen av UICollectionView, hur FlowLayout och CompositionalLayout fungerar, och hur man skapar anpassade celler i Swift. På IT Sectr använder vi UICollectionView för gallerier, kataloger och anslagstavlor. För jämförelse med tabeller, läs artikeln om UITableView.
Huvudpunkter
UICollectionView — är en klass från UIKit-ramverket, introducerad i iOS 6 (2012). Den visar en ordnad samling celler (UICollectionViewCell) i en flexibel layout som hanteras av ett UICollectionViewLayout-objekt. UICollectionView löser samma uppgift som UITableView (listor), men med godtycklig placering av element: rutnät, karuseller, mosaiker, kaskader.
Arkitektur UICollectionView är byggd på MVC-mönstret: data — i DataSource, layout — i UICollectionViewLayout, beteende — i UICollectionViewDelegate. Denna separation gör det möjligt att byta layouter utan att ändra datakoden. Enligt Apple (WWDC 2025) använder 72% av iOS-apparna i topp 100 på App Store UICollectionView för att visa innehåll. CollectionView stöder flerkolumnslayouter från iOS 6 och från iOS 13 fick den CompositionalLayout för komplexa sektioner.
UICollectionView använder en pool av återanvändbara celler (reuse queue), liknande UITableView. Metoden dequeueReusableCell(withReuseIdentifier:for:) returnerar en cell från poolen eller skapar en ny om poolen är tom. Till skillnad från UITableView har UICollectionViewCell en inbyggd contentView som alla subviews måste läggas till i — lägg inte till dem direkt i cellen, eftersom contentView ansvarar för korrekt animering av markering och redigering.
UICollectionViewFlowLayout — standardlayout som fungerar från iOS 6. Den placerar element i rader (flow) med möjlighet att ställa in cellstorlek, scrollriktning, minimala mellanrum och sektioner. UICollectionViewCompositionalLayout — modern layout (iOS 13+) som beskriver layouten genom kombination av block: grupp → sektion → layout, där varje grupp kan vara horisontell, vertikal eller anpassad.
| Parameter | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Minsta version | iOS 6 | iOS 13 |
| Komplexa layouter | Endast enkelt rutnät eller lista | Sektioner med olika layouter, grupper, karuseller |
| Anpassningsbarhet | Genom delegerad storlek | NSCollectionLayoutDimension.fractionalWidth/Height |
| Prestanda | Renderar alla synliga element | Lat skapande av sektioner |
| iPad-stöd | Kräver manuell anpassning | Anpassningsbara grupper via fractionalWidth |
| Skrivkomplexitet | Låg (5–10 rader) | Medel (15–30 rader) |
// Grundläggande UICollectionView med 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
}
}
Apples rekommendation (WWDC 2025): för nya projekt använd CompositionalLayout. Det ger fler möjligheter för anpassningsbara layouter, fungerar bättre med UIKit och SwiftUI via UICollectionView.Representable och stöder sektionsrubriker med automatisk storlek. Lämna FlowLayout för enkla skärmar med rutnät av identiska celler och minsta version iOS 12 och lägre.
UICollectionViewCell — basklass för UICollectionView-celler. Varje cell innehåller contentView (huvudbehållare), backgroundView (standardbakgrund), selectedBackgroundView (bakgrund vid markering). Anpassa cellens utseende inuti contentView — lägg aldrig till subviews direkt i UICollectionViewCell.
// Anpassad cell med konfiguration
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 tillhandahåller metoder för hantering av markering, högdagring, snabbmenyer och omordning. Till skillnad från UITableView kan UICollectionViewDelegate inte bara hantera klick på ett element, utan också långtryckning (snabbmeny via UIContextMenuInteraction). För att hantera klick, implementera collectionView(_:didSelectItemAt:) — den anropas när markeringsanimeringen är klar.
UICollectionViewDiffableDataSource — typsäker ersättning för UICollectionViewDataSource, introducerad i iOS 13. Istället för manuell hantering av celler via cellForItemAt använder DiffableDataSource NSDiffableDataSourceSnapshot — en ögonblicksbild av datatillståndet som automatiskt beräknar skillnaden mellan gammalt och nytt tillstånd och animerar ändringarna.
// DiffableDataSource med sektioner
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)
}
// Uppdatering med automatisk animering
func updateProducts(_ products: [Product]) {
var snapshot = dataSource.snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers)
snapshot.appendItems(products)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
Fördelar med DiffableDataSource: (1) automatisk animering — insert, delete, reload sker med standardanimering; (2) typsäkerhet — sektioner och element är typade, kompileringsfel istället för runtime-fel; (3) Snapshot — ögonblicksbilden kan sparas, ångras, jämföras; (4) prestanda — DiffableDataSource använder Hellir-algoritmen för att beräkna minsta uppsättning ändringar. Apple (WWDC 2024) rekommenderar DiffableDataSource för alla nya UICollectionView och lämna den gamla DataSource endast för iOS 12 och äldre.
UICollectionViewLayout — abstrakt klass som helt styr placeringen av samlingselement. Om FlowLayout och CompositionalLayout inte passar (till exempel cirkulär layout, spiral, diagram), skapa en underklass till UICollectionViewLayout och åsidosätt metoderna för layoutförberedelse.
// Anpassad cirkulär layout
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]
}
}
Prestanda: anpassad UICollectionViewLayout bör cachelagra attribut i prepare() och returnera dem i O(1) i layoutAttributesForItem(at:). För samlingar med 1000+ element, använd inkrementell cacheuppdatering via shouldInvalidateLayout(forBoundsChange:) — detta gör att du inte behöver räkna om hela layouten vid scrollning. Apple (WWDC 2023) rekommenderar CompositionalLayout istället för anpassade layouter: den täcker 95% av scenarierna utan att behöva skriva en layout från grunden.
UITableView och UICollectionView — två huvudkomponenter i UIKit för visning av listor. UITableView är enklare och avsedd för enkolumniga listor. UICollectionView är mer flexibel och stöder vilken layout som helst. Valet mellan dem beror på den visuella strukturen av data och den anpassning som krävs.
| Parameter | UITableView | UICollectionView |
|---|---|---|
| Layout | Endast vertikal lista | Lista, rutnät, kaskad, karusell, anpassad |
| Komplexitet | Låg (enkel DataSource) | Medel (Layout + DataSource + Delegate) |
| Standardcell | 4 stilar (basic, subtitle, value1, value2) | Endast tom (anpassad via contentView) |
| Redigeringsläge | Inbyggd (delete, move, insert) | Kräver manuell implementering |
| Header/Footer | Inbyggd (viewForHeaderInSection) | Via kompletterande vyer |
| Prestanda | Hög för listor | Hög för alla layouter |
| iOS 6+ | Tillgänglig från iOS 2 | Tillgänglig från iOS 6 |
Praktisk regel: använd UITableView om skärmen är en enkel lista med rader (inställningar, kontakter, meny). Använd UICollectionView om skärmen kräver ett rutnät, horisontell scrollning, karusell, olika cellstorlekar eller komplex gruppering. Från iOS 14 har UITableView också fått stöd för CompositionalLayout via UICollectionLayoutListConfiguration, vilket gör det möjligt att kombinera fördelarna med båda komponenterna.
Vanliga frågor
Ställ in scrollDirection = .horizontal i UICollectionViewFlowLayout. För CompositionalLayout använd NSCollectionLayoutSize med fractionalWidth för anpassningsbar gruppbredd. Horisontell scrollning är bra för karuseller, gallerier och kategorier. För sidvis scrollning, ställ in collectionView.isPagingEnabled = true eller använd UICollectionViewFlowLayout med itemSize = bounds.width.
Kontrollera: (1) cellen är registrerad via register(_:forCellWithReuseIdentifier:), (2) DataSource returnerar antal element > 0, (3) cellen är dequeued med korrekt reuseIdentifier, (4) itemSize är inte noll (för FlowLayout). Typiskt fel — itemSize = CGSize.zero, där celler har noll höjd. Ställ in itemSize explicit eller implementera delegerad collectionView(_:layout:sizeForItemAt:).
För FlowLayout: layout.minimumInteritemSpacing (mellan element i en rad) och layout.minimumLineSpacing (mellan rader). För CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing och contentInsets på grupp/sektion. sectionInset ställer in sektionens yttre mellanrum. Använd sectionInsetReference = .fromContentInset för korrekt hänsyn till Safe Area.
Använd performBatchUpdates för gruppanimeringar: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. För säker och animerad uppdatering, använd UICollectionViewDiffableDataSource: anropa dataSource.apply(snapshot, animatingDifferences: true) — alla ändringar beräknas och tillämpas automatiskt utan manuella insert/delete.
I SwiftUI är motsvarigheten till UICollectionView LazyVGrid och LazyHGrid (iOS 14+). SwiftUI Grid är enklare att skriva (deklarativ syntax), men är sämre än UICollectionView i prestanda för 500+ element och layoutanpassning. För komplexa samlingar, använd UICollectionView via UIViewRepresentable. I IT Sectr-projekt väljer vi UICollectionView för kataloger och gallerier, SwiftUI Grid för enkla skärmar.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också