Hacking with Swift – Consolidation V

This challenge tasks us with building an app that lets users:

  • take photos of things that interest them
    • add captions to those photos
  • show them in a table view
    • tapping the caption should show the picture in a new view controller

The order of things

First of all I want to plan ahead what needs to be done.

  1. Create the basic table-view-controller that loads rows from a captions string array.
  2. Add a ‘+’ button for picking the image and add an action method
  3. Design the detail-view-controller in Interface Builder with an ImageView and connect the elements.
  4. Create properties and implement TableView Data Source and Delegate methods.
  5. Prepare the detail-view-controller to receive the image and configure the picker.
  6. Create new custom type for the Picture so that it contains both image name and caption and adjust the TableView methods
  7. Set up what is happening when the camera has finished.
  8. Give the user a way to edit the caption in the Detail View Controller
  9. Save and load the Data
  10. Optionally follow points 21+ for delegation

What I am doing

I will keep track here of what I am doing so that I can trace back my steps and see where the errors may be.

  1. Change ViewController class inheritance to UITableViewController
  2. In Main.storyboard: cancel the present view controller, drag a new table-view controller, make it load from our ViewController class, set it as Is Initial View Controller, embed it in a Navigation Controller. Then, select the navigation controller and, in the Attributes Inspector, check “Prefers Large Titles”. Select the table-view cell and give it an identifier of “Caption”. Add a bar button item to the top right of the view controller and change its System Item to Camera.
  3. Switch to the Assistant Editor and create an action called “cameraTapped” on this button.
  4. Switch to Standard Editor and create a new view controller. In Attributes Inspector give it a title of “Detail View Controller” (for the document outline) and, in the Identity Inspector, give it a Storyboard ID of “Detail” to be able to call it back in code.
  5. Drag an UIImageView to the detail-view-controller and pin it to all sides of the view. In the Attributes Inspector change its content mode to “Aspect Fit”.
  6. Create a new Cocoa Touch Class based on UIViewController and call it “DetailViewController”. Make the appropriate view controller adopt this class and connect the image view via an outlet to the Swift file.
  7. In ViewController.swift add a var captions = [String](); implement the numberOfRowsInSection method with a return value of captions.count; implement the cellForRowAt method with the dequeuing of the “Caption” cell, the setting of its cell.textLabel?.text to captions[indexPath.row]; add the didSelectRowAt method and instantiate the new view controller with a selectedImage property, a title (both pointing to the same string by now) and pushing it.
  8. In DetailViewController.swift add the var selectedImage: String? line to avoid errors. Build and Run to check the app is working up until now.

Remember to put some test value inside the array otherwise nothing will work and you will start to make panic (as I did!). I simply added a loop for 100 or so numeric values in viewDidLoad to append to the captions array. So far, so good.

  1. In DetailViewController.swift add the image loading conditional binding to viewDidLoad. Optional, implement the viewWillAppear and viewWillDisappear methods to hide the navigation bars and allow for full viewing of the image.
  2. In ViewController.swift add conformance to UIImagePickerControllerDelegate and UINavigationControllerDelegate; configure the picker in the cameraTapped action method.
  3. Create a new CocoaTouch Class for the Picture custom type, a class that subclasses NSObject and conforms to the Codable protocol. Give it a caption and image String variables and a class initialiser; modify ViewController.swift to have two separate arrays of Strings, one for image names and one for captions; perform the necessary changes in viewDidLoad and in the table-view methods.
  4. Delete the two properties at the top of ViewController.swift and replace them with a single var pictures = [Picture](). Modify the numberOfRows method accordingly and set the textLabel to respond to the image property and the detailTextLabel to the caption. In the didSelectRowAt do the same for the selectedImage property and the title one. This will probably change again later. No idea how things will turn out now.
  5. Write the didFinishPickingMediaWithInfo method plus the helper getDocumentsDirectory method. This is still incomplete as it is missing save functionality.
  6. Inside DetailViewController.swift, create a right-bar-button-item with the “Compose” style and attach an editCaption method to it. Now this allows me to change the title of this view-controller but it does not change the subtitle of our original cell.
  7. Inside ViewController.swift cancel the loading loop inside viewDidLoad and add a title = "Captioned" call!
  8. Give permissions to use the camera in the Info.plist file. Build and run; the app launches to an empty table-view (which seems good), the camera button works, the capturing does as well but then nothing gets created in the table-view… mmm… –
  9. Inside DetailViewController’s viewDidLoad don’t forget to load the image from the path where it was saved… just using the String will not work… How was I supposed to know that only Zeus knows but fine…
  10. Create the save method and call it wherever you reload the table views.
  11. Create a load method.
  12. Now, create a new branch to finish the challenge in the ordinary way
  13. Checkout to the old branch, erase the bar button item from the detail view controller and the accompanying method and, back in ViewController.swift complete the didFinishPickingMediaWithInfo method with a closure attached to the dismiss method. This will just work.
  14. Checkout to the new branch which should revert the working directory to what it was before and proceed as follows.
  15. In DetailViewController.swift, above the class declaration, add the following protocol:
protocol DetailViewControllerDelegate: class {
	func detailViewController(_ controller: DetailViewController, didFinishEditing item: Picture)
}
  1. In the viewWillDisappear method add this code:
if let picture = selectedPicture {
	delegate?.detailViewController(self, didFinishEditing: picture)
}
  1. In ViewController.swift, in didSelectRowAt add detailVC.delegate = self
  2. After the ViewController’s class add the following extension:
extension ViewController: DetailViewControllerDelegate {
    func detailViewController(_ controller: DetailViewController, didFinishEditing item: Picture) {
        if let index = pictures.firstIndex(of: item) {
            let indexPath = IndexPath(row: index, section: 0)
            if let cell = tableView.cellForRow(at: indexPath) {
                cell.textLabel?.text = pictures[indexPath.row].caption
                cell.detailTextLabel?.text = pictures[indexPath.row].image
                save()
            }
        }
    }
} 

Done!

Plenty of ways to do it better? Sure! Does it work as it is now? Yes it does and it is awesome!

The only thing I don’t understand is why Xcode made such a mess with my branching and git repositories. So… if you want the version without the extension, this is the repo you want to check, otherwise go here.

As always, thank you so much for keeping up with me!

See you on the next one!


If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).

If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).

You can also support me by buying Paul Hudson’s books from this Affiliate Link.

Anyways, thank you so much for reading!

Till the next one!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: