A student that completes this project shows that they can:
- understand and explain what a collection view is along with common scenarios for its use
- implement UICollectionViewDataSource methods to populate a collection view from model data
- implement and customize basic layouts using UICollectionViewFlowLayout
- implement custom UICollectionViewCells
- implement UICollectionViewDelegate method to react to selecting a collection view cell
- understand and explain the purpose of UserDefaults and the kind of information it should be used to store
- define keys for specific values stored in UserDefaults
- use UserDefaults to store and retrieve preferences
This application allows a user to add photos from their photo library to a collection of photos in a UICollectionView
. It will also let the user select a theme and it will persist that preference between launches of the app using UserDefaults
Please look at the screen recording below to know what the finished project should look like:
Please fork and clone this repository. This repository does not have a starter project, so create one inside of the cloned repository folder. Be regularly committing and pushing your code as appropriate.
- Create a swift file called "Photo.swift".
- Create a struct
Photo
in it with the following properties:- An
imageData: Data
variable. - A
title: String
variable.
- An
- Adopt the `Equatable Protocol.
- Create a swift file called "PhotoController.swift".
- Create a class
PhotoController
. - Add a
photos
varable of type[Photo]
, and set its initial value to an empty array. - Add a "Create" method that initializes a new instance of
Photo
and appends it to thephotos
array. - Add an "Update" method that takes in
Photo
,Data
, andString
parameters.
You will now make a helper class that will contain functionality related to the user's theme preference and give access to that theme information throughout the application. It will use UserDefaults
to save and load this preference.
- Create a "ThemeHelper.swift" file. Add a
ThemeHelper
class inside of it. 2. Create a string constant calledthemePreferenceKey
. This will be used whenever you need to save the user's theme preference or get it fromUserDefaults
. - Create a function called
setThemePreferenceToDark()
. Using theset
method ofUserDefaults
(remember that in order to accessset
, you have to use thestandard
property onUserDefaults
), set the string "Dark" as the value, and thethemePreferenceKey
as the key. - Create another function that does the same thing, but with a different color. Call the function
setThemePreferenceToYourColorHere
. - Finally, create a computed property called
themePreference
of typeString?
. This should simply return the string value that you stored from either of the two methods you wrote above. Use theUserDefaults
string(forKey: ...)
method to get that value. The method should return an optional string because if the user's theme preference hasn't been saved, the value returned fromUserDefaults
will benil
. - In the initializer for this class (you will have to call it), check if the
themePreference
value is nil. If it is, then call one of thesetThemePreference
functions you just created. This will make it so the first time the user opens the app a preference will be set for them until they choose to change it.
Keep in mind that the implementation of this class is not the absolute best. You haven't been introduced to a few concepts that would facilitate and keep this class a bit cleaner, especially if this were to be expanded into a full theme helper class for an actual application. Don't worry though, we'll get there!
The layout of this application uses the master-detail pattern. It also includes a view controller that will allow the user to change the application's theme.
Delete the view controller scene that comes with the Main.storyboard.
- Add a
UICollectionViewController
scene. Embed it in a navigation controller and set the navigation controller as the initial view controller. - Create a Cocoa Touch subclass of
UICollectionViewController
calledPhotosCollectionViewController
. Set the collection view controller scene's class to it. - Resize the collection view cell however you want, then add a label and an image view to it.
- Create a Cocoa Touch subclass of
UICollectionViewCell
calledPhotosCollectionViewCell
. Set the cell's class to it, then make outlets from the label and image view. - Add a bar button item in the left of the navigation bar and set its title to "Select Theme". Add another bar button item in the right of the navigation bar and set its "System Item" to "Add".
Consider changing the collection view's section insets in the Size Inspector so the cells aren't right at the edge of the screen. This is purely for aesthetic purposes.
- Add a
UIViewController
scene and create a Cocoa Touch subclass ofUIViewController
calledPhotoDetailViewController
. - Create the following variables in this subclass:
photoController: PhotoController?
photo: Photo?
themeHelper: ThemeHelper?
- Set the view controller scene's class to
PhotoDetailViewController
. - Add a
UIImageView
, aUIButton
, and aUITextField
. Change the button's title to "Add Photo" - Add a
UINavigationItem
then aUIBarButtonItem
to the right of the navigation bar. Set its "System Item" to "Save". - Create outlets from the image view and text field.
- Create an action from the "Add PHoto" button called
addPhoto
, and an action from the bar button item calledsavePhoto
. - Create a "Show" segue from the "Add" bar button item in the collection view scene to this view controller. Give it an identifier.
- Create another "Show" segue to this view controller from the collection view cell. Give it an identifier.
- Add another
UIViewController
scene and create a Cocoa Touch subclass ofUIViewController
calledThemeSelectionViewController
. - Create a
themeHelper: ThemeHelper?
variable in this subclass. - Set the view controller scene's class to
ThemeSelectionViewController
. - Add a label that tells the user to select a theme that they would like to use.
- Add two buttons. One should day "Dark" for the dark theme, and the other should say the other theme that you chose to make in the
ThemeHelper
such as "Blue". - Add an action from each button. Call them
selectDarkTheme
andselectYourColorHereTheme
. - Create a "Present Modally" segue from the "Select Theme" bar button item in the collection view scene to this view controller. Give it an identifier.
- Create a
photo: Photo?
variable. - Create an
updateViews()
function. You should be familiar with this function. - Add a
didSet
property observer to thephoto
variable. CallupdateViews
inside of it.
- Create a constant called
photoController
and set its value to a new instance ofPhotoController
. - Create a constant called
themeHelper
and set its initial value to a new instance ofThemeHelper
. - Fill in the
numberOfItemsInSection
There should be as many cells are there are photos in thephotoController
'sphotos
array. - Fill in the
cellForItemAt
method. This should pass an instance ofPhoto
to the custom cell. Note: You will need to cast the cell asPhotoCollectionViewCell
.
Now, we're going to use the ThemeHelper
class to let us know what theme the user has selected and make a function that will change the appearance of the view controller based on the theme.
-
Create a function called
setTheme()
. This function should:- Get the current
themePreference
from thethemeHelper
. Make sure that it has a value, or return out of the function. - Based on the value, change the collection view's background color depending on whether the theme is dark or the other color you selected.
- Get the current
-
Implement the
prepareForSegue
. You should have three segues to handle.- The segue from the cell should pass the
themeController
,photoController
, and thephoto
. - The segue from the "Add" bar button item should pass the the
themeController
and thephotoController
. - The segue from the "Select Theme" bar button item should pass the
themeController
.
- The segue from the cell should pass the
This view controller will allow the user to (re)select their desired theme.
- In the
selectDarkTheme
action, call thethemeHelper
'ssetThemePreferenceToDark
method. Then calldismiss(animated: Bool, completion: ...)
. Setanimated
totrue
andcompletion
tonil
. - Do the same thing for the other action, but calling the other
setThemePreference
method in thethemeHelper
.
- Create a
setTheme
function. This should do the same thing as thesetTheme
method in your collection view controller, except that it should change the view controller'sview
's background color instead. - Create an
updateViews
function. Call thesetTheme
function at the first of this function. that takes the values in thephoto
(if it isn'tnil
) and sets them in the appropriate UI element. You will need to use theUIImage(data: Data)
initializer to convert thephoto
'simageData
to aUIImage
. You can then put thatUIImage
in the image view. Call it in theviewDidLoad
method of the view controller. - The "Save" bar button item's action should either update the
photo
if it has a value, or create a new instance ofphoto
using the methods in thephotoController
. "Pop" the view controller afterwards. - The
addImage
action should present aUIImagePickerController
that allows the user to select an image to add to thePhoto
object.- Note: Make sure you request authorization to access the photo library, and add the "Privacy - Photo Library Usage Description" key-value pair in the info.plist.
- Note: You will need to adopt the
UIImagePickerControllerDelegate
and implement thedidFinishPickingMediaWithInfo
method to get the image the user selects, then dismiss the image picker. You will also need to adopt theUINavigationControllerDelegate
.
- Add persistence using
Codable
,PropertyListEncoder
,PropertyListDecoder
, andFileManager
to save a plist of all the user'sMemory
objects. - Experiment with the methods that
UICollectionViewDelegateFlowLayout
has to customize your collection view's layout.