UICollectionView — ένα ισχυρό στοιχείο του UIKit για iOS που διαχειρίζεται την εμφάνιση συλλογών αντικειμένων με τη μορφή πλεγμάτων, λιστών και προσαρμοσμένων διατάξεων. Σε αντίθεση με το UITableView, το UICollectionView δεν περιορίζεται σε μία στήλη: υποστηρίζει αυθαίρετη τοποθέτηση στοιχείων μέσω του UICollectionViewLayout. Εξηγούμε την ουσία του UICollectionView, πώς λειτουργούν τα FlowLayout και CompositionalLayout, και πώς να δημιουργείτε προσαρμοσμένα κελιά σε Swift. Στην IT Sectr χρησιμοποιούμε UICollectionView για γκαλερί, καταλόγους και πίνακες ανακοινώσεων. Για σύγκριση με πίνακες, διαβάστε το άρθρο για το UITableView.
Κύρια σημεία
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 είναι υπεύθυνο για τη σωστή κινούμενη εικόνα επιλογής και επεξεργασίας.
UICollectionViewFlowLayout — τυπική διάταξη που λειτουργεί από το iOS 6. Τοποθετεί στοιχεία σε σειρές (flow) με δυνατότητα ρύθμισης μεγέθους κελιού, κατεύθυνσης κύλισης, ελάχιστων διαστημάτων και ενοτήτων. UICollectionViewCompositionalLayout — σύγχρονη διάταξη (iOS 13+) που περιγράφει τη διάταξη μέσω συνδυασμού μπλοκ: ομάδα → ενότητα → διάταξη, όπου κάθε ομάδα μπορεί να είναι οριζόντια, κάθετη ή προσαρμοσμένη.
| Παράμετρος | UICollectionViewFlowLayout | UICollectionViewCompositionalLayout |
|---|---|---|
| Ελάχιστη έκδοση | iOS 6 | iOS 13 |
| Σύνθετες διατάξεις | Μόνο απλό πλέγμα ή λίστα | Ενότητες με διαφορετικές διατάξεις, ομάδες, καρουζέλ |
| Προσαρμοστικότητα | Μέσω μεγέθους εκπροσώπου | NSCollectionLayoutDimension.fractionalWidth/Height |
| Απόδοση | Αποδίδει όλα τα ορατά στοιχεία | Τεμπέλης δημιουργία ενοτήτων |
| Υποστήριξη iPad | Απαιτεί χειροκίνητη προσαρμογή | Προσαρμοστικές ομάδες μέσω fractionalWidth |
| Πολυπλοκότητα γραφής | Χαμηλή (5–10 γραμμές) | Μεσαία (15–30 γραμμές) |
// Βασικό 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 — βασική κλάση για κελιά UICollectionView. Κάθε κελί περιέχει contentView (κύριο δοχείο), backgroundView (προεπιλεγμένο φόντο), selectedBackgroundView (φόντο κατά την επιλογή). Προσαρμόστε την εμφάνιση του κελιού μέσα στο contentView — μην προσθέτετε ποτέ subview απευθείας στο UICollectionViewCell.
// Προσαρμοσμένο κελί με διαμόρφωση
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 — ασφαλής ως προς τύπο αντικατάσταση του UICollectionViewDataSource, που παρουσιάστηκε στο iOS 13. Αντί για χειροκίνητη διαχείριση κελιών μέσω cellForItemAt, το DiffableDataSource χρησιμοποιεί NSDiffableDataSourceSnapshot — ένα στιγμιότυπο της κατάστασης δεδομένων που υπολογίζει αυτόματα τη διαφορά μεταξύ παλιάς και νέας κατάστασης και κινουμένης εικόνας τις αλλαγές.
// 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 χρησιμοποιεί τον αλγόριθμο Hellir για υπολογισμό του ελάχιστου συνόλου αλλαγών. Η Apple (WWDC 2024) συνιστά το DiffableDataSource για όλα τα νέα UICollectionView, και αφήστε την παλιά DataSource μόνο για iOS 12 και παλαιότερα.
UICollectionViewLayout — αφηρημένη κλάση που ελέγχει πλήρως τη διάταξη των στοιχείων συλλογής. Εάν τα FlowLayout και CompositionalLayout δεν είναι κατάλληλα (για παράδειγμα, κυκλική διάταξη, σπείρα, διάγραμμα), δημιουργήστε μια υποκλάση του UICollectionViewLayout και παρακάμψτε τις μεθόδους προετοιμασίας διάταξης.
// Προσαρμοσμένη κυκλική διάταξη
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% των σεναρίων χωρίς να χρειάζεται να γράψετε διάταξη από το μηδέν.
UITableView και UICollectionView — δύο κύρια στοιχεία UIKit για εμφάνιση λιστών. Το UITableView είναι απλούστερο και προορίζεται για λίστες μιας στήλης. Το UICollectionView είναι πιο ευέλικτο και υποστηρίζει οποιαδήποτε διάταξη. Η επιλογή μεταξύ τους εξαρτάται από την οπτική δομή των δεδομένων και την απαιτούμενη προσαρμογή.
| Παράμετρος | UITableView | UICollectionView |
|---|---|---|
| Διάταξη | Μόνο κάθετη λίστα | Λίστα, πλέγμα, καταρράκτης, καρουζέλ, προσαρμοσμένη |
| Πολυπλοκότητα | Χαμηλή (απλό 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, που επιτρέπει το συνδυασμό των πλεονεκτημάτων και των δύο στοιχείων.
Συχνές ερωτήσεις
Ορίστε scrollDirection = .horizontal στο UICollectionViewFlowLayout. Για CompositionalLayout χρησιμοποιήστε NSCollectionLayoutSize με fractionalWidth για προσαρμοστικό πλάτος ομάδας. Η οριζόντια κύλιση είναι καλή για καρουζέλ, γκαλερί και κατηγορίες. Για κύλιση ανά σελίδα, ορίστε collectionView.isPagingEnabled = true ή χρησιμοποιήστε UICollectionViewFlowLayout με itemSize = bounds.width.
Ελέγξτε: (1) το κελί έχει καταχωρηθεί μέσω register(_:forCellWithReuseIdentifier:), (2) το DataSource επιστρέφει αριθμό στοιχείων > 0, (3) το κελί έχει γίνει dequeue με σωστό reuseIdentifier, (4) το itemSize δεν είναι μηδέν (για FlowLayout). Τυπικό σφάλμα — itemSize = CGSize.zero, όπου τα κελιά έχουν μηδενικό ύψος. Ορίστε ρητά το itemSize ή υλοποιήστε τον εκπρόσωπο collectionView(_:layout:sizeForItemAt:).
Για FlowLayout: layout.minimumInteritemSpacing (μεταξύ στοιχείων σε μια σειρά) και layout.minimumLineSpacing (μεταξύ σειρών). Για CompositionalLayout: NSCollectionLayoutGroup.interItemSpacing και contentInsets στην ομάδα/ενότητα. Το sectionInset ορίζει τα εξωτερικά διαστήματα της ενότητας. Χρησιμοποιήστε sectionInsetReference = .fromContentInset για σωστή συνεκτίμηση του Safe Area.
Χρησιμοποιήστε performBatchUpdates για κινούμενη εικόνα ομάδας: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. Για ασφαλή και κινούμενη ενημέρωση, χρησιμοποιήστε UICollectionViewDiffableDataSource: καλέστε dataSource.apply(snapshot, animatingDifferences: true) — όλες οι αλλαγές θα υπολογιστούν και θα εφαρμοστούν αυτόματα χωρίς χειροκίνητα insert/delete.
Στο SwiftUI, το αντίστοιχο του UICollectionView είναι LazyVGrid και LazyHGrid (iOS 14+). Το SwiftUI Grid είναι απλούστερο στη γραφή (δηλωτική σύνταξη), αλλά υστερεί σε σχέση με το UICollectionView σε απόδοση για 500+ στοιχεία και προσαρμογή διατάξεων. Για σύνθετες συλλογές, χρησιμοποιήστε UICollectionView μέσω UIViewRepresentable. Στα έργα IT Sectr επιλέγουμε UICollectionView για καταλόγους και γκαλερί, SwiftUI Grid για απλές οθόνες.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης