28. Juli 2019

UICollectionView-Tutorial: Foto-Grid implementieren

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

UICollectionView Darstellung

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 28.7.2019 mit Xcode 10.3).

  2. Lade den Start-Stand von dem Beispielprojekt Countries. Dieses enthält einige Beispieldaten so dass Du Dich in diesem Tutorial auf die Implementiertung des UICollectionViewControllers fokussieren kannst.
    Mache Dich kurz mit den Beispieldaten im 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:

    Beispieldaten im Projekt
  3. Entferne den bestehenden ExampleViewController aus dem Storyboard. Lösche auch die zugehörige Swift-Datei ExampleViewController.

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

    Collection View Controller hinzufügen
  5. Ziehe die Zelle mit gedrückter Shift-Taste größer:

    Zelle vergrößern
  6. Füge in der Zelle ebenfalls über die Object Library ein Image View ein. Achte darauf, das das Image View in der Collection View Cell landet:

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

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

    Reuse Identifier für Zelle vergeben
  9. 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
  10. Erstelle per Rechtsklick auf die Controller-Gruppe im Project Navigator eine neue Cocoa Touch Class GalleryCollectionViewController, die von UICollectionViewController erbt.

  11. 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.
        }
    
        // ...
    
    }
  12. Deklariere oberhalb der GalleryCollectionViewController-Klasse eine neue Klasse GalleryImageCell für die Zelle:

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

    Klasse für Controller im Storyboard angeben
  14. Gib die erstellte UICollectionViewCell- Klasse im Storyboard als Klasse für die Zelle an:

    Klasse für Zelle im Storyboard angeben
  15. Öffne die Klasse im Assistant Editor und erstelle ein Outlet für das Image View in GalleryImageCell (wenns nicht geht, ist entweder die Zell-Klasse nicht korrekt angegeben oder ein Xcode-Neustart notwendig):

    Outlet für Image View erstellen
  16. 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
        }
        
    }
  17. Starte die App und Teste die Anzeige der Bilder:

    Image Results

  18. Optional kann das Layout noch verbessert werden. Setze dazu dem Image View 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()
        }
    }
        
        // ...
        
    }
  19. Prüfe das Ergebnis:

    UICollectionView Darstellung