NEW! Master Swift design patterns with my latest book! >>

< 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 {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Person", for: indexPath) as! 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(red: 0, green: 0, blue: 0, alpha: 0.3).cgColor
    cell.imageView.layer.borderWidth = 2
    cell.imageView.layer.cornerRadius = 3
    cell.layer.cornerRadius = 7

    return cell
}

The only new thing in there is setting the cornerRadius property, which rounds the corners of a CALayer – or in our case the UIView being drawn by the CALayer.

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) { [unowned self, ac] _ in
        let newName = ac.textFields![0]
        person.name = newName.text!

        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!

Need to know Objective-C fast?

I wrote a book dedicated to teaching Objective-C to developers who already know Swift – it's the fastest way to get up to speed!

< Previous: Custom subclasses of NSObject   Next: Wrap up >
MASTER SWIFT NOW
Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 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 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!

Click here to visit the Hacking with Swift store >>