UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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!

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.9/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.