Collection View Data Source

Applications are constantly changing, so part of being a good iOS developer is building applications in a way that allows them to adapt to changing requirements.

The Photorama application will display a single collection view of photos. You could do something similar to what you did in LootLogger and make the PhotosViewController be the data source of the collection view. The view controller would implement the required data source methods, and everything would work just fine.

At least, it would work for now. What if, sometime in the future, you decided to have a different screen that also displayed a collection view of photos? Maybe instead of displaying the interesting photos, it would use a different web service to display all the photos matching a search term. In this case, you would need to reimplement the same data source methods within the new view controller with essentially the same code. That would not be ideal.

Instead, you will abstract out the collection view data source code into a new class. This class will be responsible for responding to data source questions – and it will be reusable if necessary.

Create a new Swift file named PhotoDataSource and declare the PhotoDataSource class.

Listing 21.1  Creating the PhotoDataSource class (PhotoDataSource.swift)

import Foundation
import UIKit

class PhotoDataSource: NSObject, UICollectionViewDataSource {

    var photos = [Photo]()

}

To conform to the UICollectionViewDataSource protocol, a type also needs to conform to the NSObjectProtocol. The easiest and most common way to conform to this protocol is to subclass from NSObject, as you did above.

The UICollectionViewDataSource protocol declares two required methods to implement:

    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

You might notice that these two methods look very similar to the two required methods of UITableViewDataSource that you saw in Chapter 9. The first data source callback asks how many cells to display, and the second asks for the UICollectionViewCell to display for a given index path.

Implement these two methods in PhotoDataSource.swift.

Listing 21.2  Implementing the collection view data source methods (PhotoDataSource.swift)

class PhotoDataSource: NSObject, UICollectionViewDataSource {

    var photos = [Photo]()

    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int {
        return photos.count
    }

    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let identifier = "PhotoCollectionViewCell"
        let cell =
            collectionView.dequeueReusableCell(withReuseIdentifier: identifier,
                                               for: indexPath)

        return cell
    }
}

Next, the collection view needs to know that an instance of PhotoDataSource is the data source object.

In PhotosViewController.swift, add a property to reference an instance of PhotoDataSource and an outlet for a UICollectionView instance. Also, you will not need the imageView anymore, so delete it.

Listing 21.3  Declaring new properties for collection view support (PhotosViewController.swift)

class PhotosViewController: UIViewController {

    @IBOutlet var imageView: UIImageView!
    @IBOutlet var collectionView: UICollectionView!

    var store: PhotoStore!
    let photoDataSource = PhotoDataSource()

Without the imageView property, you will not need the method updateImageView(for:) anymore. Go ahead and remove that, too.

Listing 21.4  Removing updateImageView(_:) (PhotosViewController.swift)

func updateImageView(for photo: Photo) {
    store.fetchImage(for: photo) {
        (imageResult) -> Void in

        switch imageResult {
        case let .success(image):
            self.imageView.image = image
        case let .failure(error):
            print("Error downloading image: (error)")
        }
    }
}

Update viewDidLoad() to set the data source on the collection view.

Listing 21.5  Setting the collection view data source (PhotosViewController.swift)

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.dataSource = photoDataSource

Finally, update the photoDataSource instance with the result of the web service request and reload the collection view.

Listing 21.6  Updating the collection view with the web service data (PhotosViewController.swift)

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.dataSource = photoDataSource

    store.fetchInterestingPhotos {
        (photosResult) -> Void in

        switch photosResult {
        case let .success(photos):
            print("Successfully found (photos.count) photos.")
            if let firstPhoto = photos.first {
                self.updateImageView(for: firstPhoto)
            }
            self.photoDataSource.photos = photos
        case let .failure(error):
            print("Error fetching interesting photos: (error)")
            self.photoDataSource.photos.removeAll()
        }
        self.collectionView.reloadSections(IndexSet(integer: 0))
    }
}

The last thing you need to do is make the collectionView outlet connection.

Open Main.storyboard and navigate to the collection view. Control-drag from the Photorama view controller to the collection view and connect it to the collectionView outlet.

Build and run the application. After the web service request completes, check the console to confirm that photos were found. In the running app, there will be a grid of black squares corresponding to the number of photos found (Figure 21.4). These cells are arranged in a flow layout. A flow layout fits as many cells on a row as possible before flowing down to the next row. If you rotate the iOS device, you will see the cells fill the given area.

Figure 21.4  Initial flow layout

Initial flow layout
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.240.178