Chapter 2.  A Better Layout with UICollectionView

With iOS 6, Apple made UICollectionView available to developers. The UICollectionView is a very powerful view that enables developers to create beautiful layouts. It's very similar to the UITableView in terms of the APIs that are available and the way it handles delegation to other objects. Both have a data source and a delegate. Both make use of cells to display contents and are heavily optimized to handle huge amounts of data without slowing down.

The main difference between the two is that UICollectionView is more powerful and flexible. It provides developers with an easy-to-configure grid layout out of the box. However, you're free to create any type of layout that you desire with a UICollectionView.

In this chapter, you'll build upon the HelloContacts app that was built in the previous chapter. You will remove all of the UITableView code and swap out for a UICollectionView. This will enable you to create a more interesting grid layout to display contacts in. You'll also create a good-looking custom cell that will show a contact's image and name. To make your layout stand out, we need to explore the UICollectionViewFlowLayout and its working. This will allow us to tweak the grid layout to look a lot more interesting. Then, we'll move away from the standard grid layout and implement a custom layout that looks a lot more compelling to your users. After making the app look good with custom cells and a custom layout, we'll have a look at its performance. You'll learn how iOS 10 makes UICollectionViews perform better than ever, and we'll implement some of the available delegate methods, just as we did with UITableView.

The topics covered in this chapter are as follows:

  • Converting from a UITableView to a UICollectionView
  • Creating a custom UICollectionViewCell
  • Using UICollectionViewFlowLayout
  • Creating a custom UICollectionViewLayout
  • UICollectionView performance
  • User interactions with UICollectionView

Converting from a UITableView to UICollectionView

Displaying contacts in a list with UITableView is a fine idea. It's functional, looks alright, and people are used to seeing data displayed in a list. However, wouldn't it be nice if you had a more interesting way to display contacts, with bigger images maybe? Alternatively, maybe it would be nice to display contacts in a grid?

Interesting and compelling layouts make your users happy. They will notice that you have put some extra effort in your layout to please them. Users enjoy apps that have received some extra attention. With that said, you probably don't want to make everything in your apps into a grid layout. It's just not the best fit for all scenarios.

For the use case of displaying contacts, a grid is a good choice. The goal is to show the user's contacts in an interesting way. We're not really interested in sorting them alphabetically. If we were, we would have used a list; a list is way better at that.

To display contacts in a grid, some of the existing code must be cleaned up first. All of the table view-related code and layouts should be removed. That's what we'll cover now. When you're done cleaning up, you'll be left with a great starting point for implementing the UICollectionView. The steps we'll take for the cleanup process are as follows:

  1. Delete all the UITableView code
  2. Delete the UITableView from the storyboard and replace it with a UICollectionView
  3. Add code for the UICollectionView

Let's start off by simply deleting all of the code that relates to UITableView in ViewController.swift. This means you'll remove protocol conformance, all of the @IBOutlets, the reloadData call, and all of the delegate and data source methods. When you're done removing all of this code, you should be left with the following contents in your ViewController.swift file:

import UIKit 
import Contacts 
 
class ViewController: UIViewController { 
    var contacts = [HCContact]() 
 
    override func viewDidLoad() { 
        super.viewDidLoad() 
 
        let store = CNContactStore() 
 
        if CNContactStore.authorizationStatus(for: .contacts) == 
          .notDetermined { 
            store.requestAccess(for: .contacts, completionHandler:
             {[weak self] authorized, error in 
                if authorized { 
                    self?.retrieveContacts(fromStore: store) 
                } 
            }) 
        } else if CNContactStore.authorizationStatus(for: .contacts) == 
          .authorized { 
            retrieveContacts(fromStore: store) 
        } 
    } 
 
    func retrieveContacts(fromStore store: CNContactStore) { 
        let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, 
          CNContactImageDataKey, CNContactImageDataAvailableKey] 
        let containerId = store.defaultContainerIdentifier() 
        let predicate = CNContact.predicateForContactsInContainer(withIdentifier: 
          containerId) 
 
        contacts = try! store.unifiedContacts(matching: predicate, keysToFetch: 
          keysToFetch).map { contact in 
            return HCContact(contact: contact) 
        } 
    } 
} 

You'll probably remember that you created a ContactTableViewCell in the previous chapter. Don't remove that for now. You're going to reuse this code later when you implement the UICollectionViewCell.

Now that the code is cleaned up, it's time to clean up the interface. Open the storyboard file and delete the UITableView. You're now left with a clean view controller that only knows how to fetch contacts, nothing more. Time to add a UICollectionView so you can start working on displaying the contacts in a grid view!

Drag a UICollectionView from the Object library to your view controller, just as you did earlier with the UITableView. Make sure that it covers the entire viewport, including the navigation bar that's displayed at the top. Then, use the Reset to Suggested Constraints option from the Resolve Auto Layout Issues menu. This will add the same four constraints you've seen before. One constraint that pins the UICollectionView to the left side of the superview, one to the right side, one to the bottom, and one to the top.

Select the collection view and open the Attributes Inspector and search for the background color property. Set this property to white to give the collection view a white background color. UICollectionView has a transparent background by default, which means if you build the app without setting it, it would result in a black background. This is not the behavior we want for this app.

The last step in this phase is to connect the UICollectionView to the code. To do this, open up ViewController.swift. You need to add a single line of code, above the viewDidLoad method. The following line of code creates the @IBOutlet for the UICollectionView:

@IBOutlet var collectionView: UICollectionView! 

Now, back in the storyboard, you'll need to connect this @IBOutlet to the collection view. Do this by selecting the collection view and opening the Connections Inspector. Then, drag out a new referencing outlet to the collection view and select the collectionView from the list of options.

You have successfully replaced all of the UITableView code with some of the UICollectionView code that you'll be writing during this chapter. The collection view doesn't display any cells yet, but you'll get to that soon. Before you can start displaying cells, you should probably create a nice custom cell first. Let's do that right now.

..................Content has been hidden....................

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