- New Project -> Single View App -> Choose Swift
- When you create a new project, Xcode provides a default
ViewController.swiftfile. Start by renamingViewController.swifttoDetailViewController.swift- Click on the
ViewController.swiftfile from the Identity Inspector (⌥⌘3), highlightViewControllerin the source code, then right click -> Refactor -> Rename
- Click on the
- Head over to
Main.storyboardand drag aTable View Controllerobject onto the Storyboard.- Click on the Object Library (⇧⌘L) and drag it onto
Main.storyboard.
- Click on the Object Library (⇧⌘L) and drag it onto
- Click on the new
Table View Controllerand in the Attributes Inspector (⌥⌘4) check the "Is Initial View Controller" option. - Go to Project Navigator (⌘1) and add a new file to the project
- Use the Cocoa Touch Class option then hit next
- Update "Subclass of" to
UITableViewController - Update "Class" to
MasterViewController - Click Next and then Create
- Go back to
Main.storyboardand click/select yourTable View Controllerscene - Once selected, go to the Identity Inspector (⌥⌘3) and under Class make sure you select your newly created
MasterViewController
When you subclass UITableViewController it brings in a lot of pre-wired functionality. The UITableViewController subclass creates a UITableView and also sets itself as the delegate and data source of the table view. If you were to create a UIViewController that has a table view and does not inherit from UITableViewController you would have to set up the data source and the delegate of your table view yourself.
UITableView declares two protocols: UITableViewDataSource and UITableViewDelegate. The delegate protocol is used to inform the class about cells that have been selected and to provide an interface for modifying the table view behavior. The data source protocol is used by the table view to determine the content it needs to display. The delegate is there to respond to events from and guide the behavior of the table view, and the data source is there to provide data rather than control it.
In other words, UITableViewController comes with boilerplate code ready for you to use and also comes pre-wired to implement the UITableViewDataSource and UITableViewDelegate protocols.
-
Create a new swift file which we will use to declare our
Notemodel- File -> New -> File -> Swift File
- Name the file
Note.swift - Inside this file we will declare our model as a class called
Notefor storing information about Notes:class Note { var content: String? let dateCreated = Date() init(withContent content: String) { self.content = content } }
Notice the
init()function has both an argument label (withContent) and a parameter name (content). The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function.
-
Now lets create our
notesmodel inside ofMasterViewControllerby creating an array ofNote's- Add the following code just below
class MasterViewController: UITableViewController {:var notes = [Note]()
- Add the following code just below
-
Inside the
viewDidLoad()lifecycle function add a fewNote's to thenotesarray:super.viewDidLoad() notes.append(Note(withContent: "Hello")) notes.append(Note(withContent: "World!"))
-
Find the
numberOfSections()table view data source function and change the return value to:1return 1- Note that
numberOfSections()is an Optional function
- Note that
-
Find the
tableView(numberOfRowsInSection)table view data source function and change it to return the count ofnotes:return notes.count
- Note that
tableView(numberOfRowsInSection)is a Required function
- Note that
-
Uncomment the
tableView(cellForRowAt indexPath)function -
In
Main.storyboardfind the "Table View Cell" in the Master View Controller scene, then inside the Attributes Inspector (⌥⌘4) give the table cell the reuse identifier:noteCellPRO TIP: Use the Document Outline sidebar section to easily highlight different UI components
-
Back in
MasterViewController.swiftcreate aStringconstant for the identifier and use it with thedequeuResuableCell()function inside of thetableView(cellForRowAt indexPath)function-
Under the
notesarray add the following:let noteCellIdentifier = "noteCell"
-
In the
cellForRowAtfunction:let cell = tableView.dequeueReusableCell(withIdentifier: noteCellIdentifier, for: indexPath)
-
-
Under "Configure the cell" comment inside the
tableView(cellForRowAt indexPath)function, set thetextLabelproperty of each table view cell equal to thecontentof yournotesmodel using the appropriate index.- You can use
indexPath.rowto give you the row or index the cell is on
cell.textLabel?.text = notes[indexPath.row].content return cell
- You can use
-
Build and run the app (⌘R). You should see 2 cells displayed with whatever content you populated the Notes array with.
A segue defines a transition between two view controllers in your app’s storyboard file. We will use segue's to transition from our MasterViewController to our DetailViewController whenever a user clicks on a tableView cell or wants to add a new note.
Segue's also provide a way to pass data from one controller to the next. In our case, we will use the segue to pass the appropriate Note from the MasteViewController to the DetailViewController.
-
In
Main.storyboardcreate a segue from theMasterViewControllertableView cell toDetailViewController.PRO TIP: Use the Document Outline sidebar section to easily highlight different UI components
- Control + Drag from the
noteCellcomponent to theDetailViewControllerscene - Select
Showin the popup to create a segue of typeShow - Click on the new segue in the storyboard then on Attributes Inspector. Give it the Identifier name:
showNote
- Control + Drag from the
-
In
MasterViewController.swiftcreate aStringconstant to match the Identifierlet showNoteSegue = "showNote"
-
Uncomment the
prepare(for segue)code towards the bottom ofMasterViewController.swift -
Run the app to test out your segue functionality
-
In
Main.storyboardadd a Text View to the Detail View Controller scene- From the Object Library (⇧⌘L) drag and drop a Text View onto the scene and adjust it to fit properly. Align it to fit within the margins.
-
Connect the Text View as an
IBOutletto theDetailViewController- Make sure you Show the Assistant Editor so you can see the Storyboard and
DetailViewController.swiftclass side by sidePRO TIP: You can hold
Option + Shiftthen click on a file to give you different presentation options - Create the outlet by control clicking and dragging from the Text View onto
DetailViewController.swift - Make sure Outlet is selected in the popup and give it the name:
textView - Once this is done, the
@IBOutletinsideDetailViewController.swiftshould look something like this:@IBOutlet weak var textView: UITextView!
- Make sure you Show the Assistant Editor so you can see the Storyboard and
-
Add a variable to
DetailViewController.swiftwhich will be used to hold yourNote:var note: Note?
- NOTE: Make sure the
varis optional?
- NOTE: Make sure the
-
Write a function called
configureView()to settextView.textequal to the value of the note that was passed in from the segue:func configureView() { if let note = note { textView.text = note.content } }
-
Finally, make sure to call your new
configureView()function from theviewDidLoad()lifecycle function
-
In
MasterViewControllerwe need to update thefunc prepare(for segue)function to properly sendNote's to theDetailViewControllerwhen we segue. -
Inside the
prepare(for segue)function we need to do two things:- Grab a reference to the
DetailViewControllerusingsegue.destinationViewController - Pass the selected
NotetoDetailViewController
- Grab a reference to the
-
The
prepare(for segue)function should look like the following:if segue.identifier == showNoteSegue { if let indexPath = tableView.indexPathForSelectedRow { let note = notes[indexPath.row] let detailViewController = segue.destination as! DetailViewController detailViewController.note = note } }
-
Build and run the app and verify that you are passing data correctly to the
DetailViewController
- In the storyboard, click the
Table View Controllerthen in the Xcode dropdown menu's go to Editor -> Embed In -> Navigation Controller.A navigation controller is a container view controller that manages one or more child view controllers in a navigation interface. They allow us to move between different view controller scenes by adding or popping view controllers from a stack. This will natively add a back button for us.
- In
MasterViewController.swiftfind theviewDidLoad()method and uncomment thenavigationItemline of code - Change the code to use the
leftBarButtonIteminstead:self.navigationItem.leftBarButtonItem = self.editButtonItem - Build and run the app. We should now have a "Edit" button on the left side of the navigation bar
- In
Main.storyboardgo to the Object Library (⇧⌘L) and find a Bar Button Item - Drag a Bar Button Item onto the right side of the navigation bar of the Master View Controller scene
- In the Attributes Inspector (⌥⌘4) make the "System Item" equal to "Add"

When a user presses the + button we want the app to create a new Note and then navigate to the detail page for editing. To accomplish this we'll need to create a new segue.
-
Create a new segue from the + bar button item to the
DetailViewControllerscenePRO TIP: Use the Document Outline sidebar section to easily highlight different UI components
- Control Drag from the
+button to theDetailViewControllerscene - Select
Showin the popup to create a segue of typeShow - Click on the new segue in the storyboard then on Attributes Inspector (⌥⌘4). Give it the Identifier name:
showNewNote
- Control Drag from the
-
In
MasterViewController.swiftcreate aStringconstant to match the Identifier- You should now have the following constants declared:
let cellIdentifier = "noteCell" let showNoteSegue = "showNote" let showNewNoteSegue = "showNewNote"
- You should now have the following constants declared:
-
Update the
prepare(for segue)function inMasterViewController.swiftto handle two different segue's and also add a newNoteto the model:if segue.identifier == showNoteSegue { if let indexPath = tableView.indexPathForSelectedRow { let note = notes[indexPath.row] let detailViewController = segue.destination as! DetailViewController detailViewController.note = note } } else if segue.identifier == showNewNoteSegue { // Insert a new Note to the model before transitioning to DetailViewController notes.insert(Note(withContent: "New note"), at: 0) let indexPath = IndexPath(row: 0, section: 0) // Insert a new row in our tableView tableView.insertRows(at: [indexPath], with: .automatic) let detailViewController = segue.destination as! DetailViewController detailViewController.note = notes[0] }
-
Build and run your app to test the + (add note) functionality
-
Note that on return to the Master View Controller the notes are not being updated...
We want to be able to edit notes in DetailViewController and have the model in MasterViewController get updated. To accomplish this we will make use of the viewWillDisappear() controller lifecycle method. Whenever the DetailViewController is about to disappear from the screen we want to update the note to reflect what is currently on the textView.
Add the following function and code to DetailViewController:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let note = note {
note.content = textView.text
}
}- Because we made our
Notemodel aclassinstead of astruct, we are passing notes by reference- This allows us to modify our model from anywhere in our app as long as we have a reference
- Build and run the app to see if it's working as intended
If you ran the app, you will have noticed that notes are still not being updated when we return to MasterViewController. This is because we still need to tell the UITableView to reload the data.
This time we'll take advantage of another view controller lifecycle method: viewWillAppear(). Add the following code to the MasterViewController class:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}To delete notes from the model we will take advantage of some of the boilerplate code the comes with UITableViewController.
-
In
MasterViewController.swiftfind thetableView(canEditRowAt IndexPath)data source function and uncomment it. -
Find the
tableView(commit edityingStyle, forRowAt indexPath)data source function and uncomment this one as well. -
Edit the
tableView(commit edityingStyle, forRowAt indexPath)function to properly delete aNotefrom your model whenever it is called:if editingStyle == .delete { // Delete the row from the data source notes.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) }
-
Build and run your app to verify you can delete cells


