NEW: Learn SwiftUI with my free YouTube video series! >>

< Previous: Custom subclasses of NSObject   Next: Wrap up >

Connecting up the people

We need to make three final changes to this project in order to finish: show the correct number of items, show the correct information inside each cell, then make it so that when users tap a picture they can set a person's name.

Those methods are all increasingly difficult, so we'll start with the first one. Right now, your collection view's numberOfItemsInSection method just has return 10 in there, so you'll see 10 items regardless of how many people are in your array. This is easily fixed:

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

Next, we need to update the collection view's cellForItemAt method so that it configures each PersonCell cell to have the correct name and image of the person in that position in the array. This takes a few steps:

  • Pull out the person from the people array at the correct position.
  • Set the name label to the person's name.
  • Create a UIImage from the person's image filename, adding it to the value from getDocumentsDirectory() so that we have a full path for the image.

We're also going to use this opportunity to give the image views a border and slightly rounded corners, then give the whole cell matching rounded corners, to make it all look a bit more interesting. This is all done using CALayer, so that means we need to convert the UIColor to a CGColor. Anyway, here's the new code:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Person", for: indexPath) as? PersonCell else {
        fatalError("Unable to dequeue PersonCell.")
    }

    let person = people[indexPath.item]

    cell.name.text = person.name

    let path = getDocumentsDirectory().appendingPathComponent(person.image)
    cell.imageView.image = UIImage(contentsOfFile: path.path)

    cell.imageView.layer.borderColor = UIColor(white: 0, alpha: 0.3).cgColor
    cell.imageView.layer.borderWidth = 2
    cell.imageView.layer.cornerRadius = 3
    cell.layer.cornerRadius = 7

    return cell
}

There are three new things in there.

First, notice how I’ve used indexPath.item rather than indexPath.row, because collection views don’t really think in terms of rows.

Second, that code sets the cornerRadius property, which rounds the corners of a CALayer – or in our case the UIView being drawn by the CALayer.

Third, I snuck in a new UIColor initializer: UIColor(white:alpha:). This is useful when you only want grayscale colors.

With that done, the app works: you can run it with Cmd+R, import photos, and admire the way they all appear correctly in the app. But don't get your hopes up, because we're not done yet – you still can't assign names to people!

For this last part of the project, we're going to recap how to add text fields to a UIAlertController, just like you did in project 5. All of the code is old, but I'm going to go over it again to make sure you fully understand.

First, the delegate method we're going to implement is the collection view’s didSelectItemAt method, which is triggered when the user taps a cell. This method needs to pull out the Person object at the array index that was tapped, then show a UIAlertController asking users to rename the person.

Adding a text field to an alert controller is done with the addTextField() method. We'll also need to add two actions: one to cancel the alert, and one to save the change. To save the changes, we need to add a closure that pulls out the text field value and assigns it to the person's name property, then we'll also need to reload the collection view to reflect the change.

That's it! The only thing that's new, and it's hardly new at all, is the setting of the name property. Put this new method into your class:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let person = people[indexPath.item]

    let ac = UIAlertController(title: "Rename person", message: nil, preferredStyle: .alert)
    ac.addTextField()

    ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))

    ac.addAction(UIAlertAction(title: "OK", style: .default) { [weak self, weak ac] _ in
        guard let newName = ac?.textFields?[0].text else { return }
        person.name = newName

        self?.collectionView.reloadData()
    })

    present(ac, animated: true)
}

Finally, the project is complete: you can import photos of people, then tap on them to rename. Well done!

LEARN SWIFTUI FOR FREE I wrote a massive, free SwiftUI tutorial collection, and also have a growing list of free SwiftUI tutorials on YouTube – get started today!

< Previous: Custom subclasses of NSObject   Next: Wrap up >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 5.0/5

Click here to visit the Hacking with Swift store >>