UICollectionView-Tutorial: Foto-Grid implementieren

Das Tutorial zeigt anhand eines Foto-Grids, wie mit UICollectionView und UICollectionViewController Darstellungen aus sich wiederholenden Cell-Views erstellt werden können.

UICollectionView Beispiel

Bildrechte:
Verwendung unter Creative Commons Lizenz
Siehe: Belgien, Frankreich, Deutschland, Griechenland, Österreich

  1. Verwende für dieses Tutorial die aktuelle Version von Xcode (dieses Tutorial wurde zuletzt getestet am 27. Oktober 2019 mit Xcode 11.1).

  2. Lade den Start-Stand von dem Beispielprojekt Countries. Dieses enthält einige Beispieldaten so dass Du Dich in diesem Tutorial auf die Implementierung des UICollectionViewControllers konzentrieren kannst.

  3. Mache Dich mit dem Projekt vertraut: Es gibt unter Model einen Typ Country, der Daten aus einer lokalen JSON-Datei lädt sowie einige Bilder in den Assets des Projektes:

    Assets im Xcode-Projekt Countries
  4. Entferne den bestehenden ExampleViewController aus dem Storyboard. Lösche auch die zugehörige Swift-Datei ExampleViewController.

  5. Füge in Main.storyboard über die Library einen neuen Collection View Controller ein:

    UICollectionViewController in Xcode Library
  6. Ziehe die Zelle größer - mit gedrückter Shift-Taste kann dabei das quadratische Seitenverhältnis beibehalten werden:

    Zelle vergrößern
  7. Füge in der Zelle ebenfalls über die Library ein Image View ein. Achte darauf, das das Image View innerhalb der Collection View Cell angelegt wird:

    UIImageView in der Zelle hinzufügen
  8. Erstelle Constraints, die das Image-View auf die Größe der Zelle skalieren:

    Constraints hinzufügen
  9. Vergib für die Zelle einen Reuse Identifier, z.B. ImageCell:

    Reuse Identifier für Zelle vergeben
  10. Setze für den View Controller die Eigenschaft Is Initial View Controller um diesen als Start-Controller für das Storyboard festzulegen:

    Initial View Controller
  11. Erstelle per Rechtsklick auf die Gruppe Controller im Project Navigator mit New File eine neue Cocoa Touch Class GalleryCollectionViewController, die von UICollectionViewController erbt.

  12. Passe die Konstante mit dem reuseIdentifier entsprechend Deiner Angabe im Storyboard an. Deklariere eine Eigenschaft countries die die Liste aller Länder via Country.allCountries holt.
    Achtung: Entferne unbedingt den Aufruf von self.collectionView!.register(...) im viewDidLoad: Dieser Code wird benötigt, um Zellen programmatisch zu erstellen und muss für die Verwendung von Zellen gemäß Storyboard entfernt werden:

    private let reuseIdentifier = "ImageCell"
    
    class GalleryCollectionViewController: UICollectionViewController {
    
        var countries = Country.allCountries
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Uncomment the following line to preserve selection between presentations
            // self.clearsSelectionOnViewWillAppear = false
    
            // Achtung, entfernen:
    // self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    
    
            // Do any additional setup after loading the view.
        }
    
        // ...
    
    }
    
  13. Deklariere oberhalb der GalleryCollectionViewController-Klasse eine neue Klasse GalleryImageCell für die Zelle:

    private let reuseIdentifier = "ImageCell"
    
    class GalleryImageCell: UICollectionViewCell {
    
    }
    
    class GalleryCollectionViewController: UICollectionViewController {
    
        // ...
    
    }
    
  14. Gib die erstellte UICollectionViewController-Klasse im Storyboard als Klasse für die Controller an:

    Klasse für Controller im Storyboard angeben
  15. Gib die GalleryImageCell-Klasse im Storyboard als Klasse für die Zelle an:

    Klasse für Zelle im Storyboard angeben
  16. Öffne den Assistant Editor:

    Assistant Editor öffnen
  17. Erstelle eine Outlet-Eigenschaft imageView für das Image View in GalleryImageCell (wenn das nicht klappt, ist entweder die Zell-Klasse nicht korrekt angegeben oder ein Xcode-Neustart notwendig):

    Outlet erstellen
  18. Implementiere die Methoden des UICollectionViewDataSource-Protokolls so, dass die Zellen gemäß der countries-Daten befüllt werden - die Country-Klasse hat eine Eigenschaft image, mit der das Bild aus den Assets geladen werden kann:

    class GalleryCollectionViewController: UICollectionViewController {
    
        // ...
    
        // MARK: UICollectionViewDataSource
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            // Methode kann alternativ entfallen
            return 1
        }
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return countries.count
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) 
                as! GalleryImageCell
    
            let country = countries[indexPath.row]
    cell.imageView.image = country.image
    
            return cell
        }
    
    }
    
  19. Starte die App und teste die Anzeige der Bilder:

    Bilderanzeige in UICollectionViewController
  20. Optional kann das Layout noch verbessert werden. Setze dazu dem Image View im Storyboard die Eigenschaft Content Mode = Aspect Fill (die Bilder füllen dann den kompletten Platz aus). Setze dem UICollectionView zudem ein UICollectionViewFlowLayout, um die Layout-Parameter anzupassen und implementiere die UIViewController-Methode viewWillTransition(to:, with:), um die Zell-Größe bei der Rotation des Gerätes entsprechend anzupassen:

    class GalleryCollectionViewController: UICollectionViewController {
    
        // ...
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let flowLayout = UICollectionViewFlowLayout()
    flowLayout.minimumInteritemSpacing = 0
    flowLayout.minimumLineSpacing = 0
    self.collectionView.collectionViewLayout = flowLayout
    updateLayout(for: self.view.bounds.size)
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        updateLayout(for: size)
    }
    
        private func updateLayout(for size: CGSize) {
        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            let width = size.width
            let count = Int(width) / 180
            let itemWidth = width / CGFloat(count)
            flowLayout.itemSize = CGSize(width: itemWidth, height: itemWidth)
            flowLayout.invalidateLayout()
        }
    }
    
        // ...
    
    }
    
  21. Prüfe das Ergebnis:

    UICollectionView Darstellung