1. Launch Xcode
2. Choose the Single View Application template
3. Make sure that only Portrait is selected.
4. Remove the View Controller Scene from the storyboard, change UIViewController to UITableViewController
5. Drag a Table View Controller into the canvas
6. Set Class type to ChecklistViewController
7. Set the Initial View Controller.
8. Adding the label to the prototype cell, also add a checkmark to the cell’s design
9. Giving the table view cell a reuse identifier
10. Use tableView(numberOfRowsInSection) protocol and tableView(cellForRowAt) protocol
11. Gets a copy of the prototype cell, Call tableView.dequeueReusableCell(withIdentifier).
12. Set the label's Tag field to 1000
13. Ask the table view cell for the view with tag 1000.
14. Looks at the value of indexPath.row, which contains the row number, and changes the label’s text accordingly
15. Add the the tableView(didSelectRowAt) method
16. Find the UITableViewCell object for the tapped row
17. Add the toggle of checkmark
18. Add the tableView.deselectRow method.
19. Added an instance variable with the text for every row
20. Added initial check type for every cell
Bug: Cell VS Row
21. For every row you have added an instance variable with the text for that row.
22. Add another five new instance variables to keep track of the “checked” state of each of the rows.
23. Merge the 5 check prototype track variable to 1 (change 22)
Bug: the data model (the “row checked” variables) and the views (the checkmarks inside the cells) are out-of-sync.
24. Set the cell’s accessoryType property to the right value in tableView(cellForRowAt)---call configure checkmark method in tableview ( cell for row at) method, just before return cell.
25. Simplify “didSelectRowAt” by letting configureCheckmark(for:at:) do some of the work.
26. Combine the text and checkmark state into a new object: make a class named checklistItem
27. Remove the old instance variables and replace them with ChecklistItem objects
28. In tableView(cellForRowAt), replace the if-statements, In tableView(didSelectRowAt), change the following lines, in configureCheckmark(for:at:), make these changes.
29. Give it a value inside a so-called initializer method:
required init?(coder aDecoder: NSCoder) {
...
super.init(coder: aDecoder) }
30. In ChecklistViewController.swift, throw away all the instance variables and replace them with a single array variable named items.
31. Simplify the table view data source and delegate methods once again.
32. Replace the configureCheckmark(for:at:) method with (for: with: checklistItem)
33. Add a new method: Configure text
34. Update tableView(cellForRowAt) and tableView(didSelectRowAt) so that it calls these new methods
35. Add the toggle checked method
36. Embed a navigation controller in the view controller
36. Add Bar Button Item into the rightside slot of the navigation bar
37. Add a new addItem method to ChecklistViewController.swift and connect it with the button. Creates the new ChecklistItem object and adds it to the end of the array
38. Tell the table view about this new row so it can add a new cell for that row.
39. Add the Swipe-to-delete method to ChecklistViewController.swift.
40. Add a new Table View Controller as the addItem screen and connect it with the add button.
41. Remove the button’s connection with the addItem action. (change 37)
42. In the segue's Attributes inspector, choose Kind: Present Modally.
43. Embed In a Navigation Controller for the new table view controller and add the cancel button and done button.
44. Make a new view controller source code file specifically for the Add Item screen and connect it to the scene
45. Add the new cancel() and done() action methods in AddItemViewController.swift
46. In the addItem view's Attributes inspector, change the Content setting from Dynamic Prototypes to Static Cells.
47. Drag a Text Field object into the cell and set the Border Style to no border.
48. Add the willselectRow at method In AddItemViewController.swift* and return nil.
49. In the storyboard, select the table view cell and go to the Attributes inspector. Set the Selection attribute to None. (may be not necessary)
50. Connect the text field with the swift file.
51. Modify the done() action to write the contents of this text field
52. Add viewWillAppear (becomefirstResponder) method to AddItemViewController.swift
53. Set the attributes for the text field.
54. Open the Connections inspector of the text field. Drag from the Did End on Exit event to the view controller and pick the done action.
55. Disable the Done button when no text has been typed yet..
56. Make the view controller a delegate for the text field:
Include UITextFieldDelegate in the class line for the view controller.
Tell the text field that it has a delegate
Implement the delegate methods
57. Add the textField(shouldChangeCharactersIn methon to AddItemViewController.swift
58. Uncheck the Enabled box of the done button attributes inspector.
Delegates in five easy steps:
59. Defines the AddItemViewControllerDelegate protocol.
60. Inside AddItemViewController, write the variable delegate to refer back to the ChecklistViewController
61. Replace the cancel() and done() actions with delegate method.
Bug: the Cancel and Done buttons. They no longer work
62. Make the ChecklistViewController suitable to play the delegate role and add the methods that are listed in AddItemViewControllerDelegate to the checklistViewController.
63. Add prepare-for-segue method to ChecklistViewController.swift, and naming the segue between the Checklists scene and the navigation controller
64. Change the implementation of the “didFinishAdding” delegate method in ChecklistViewController.swift.( copied the contents of addItem() )
65. Remove addItem() from ChecklistViewController.swift as you no longer need this method.
66. Go to the table view cell in the storyboard and in the Attributes inspector set its Accessory to Detail Disclosure.
67. Drag a new Label into the cell and place it on the left of the text label.
68. In ChecklistViewController.swift, change configureCheckmark(for:with:) (change 32)
69. Connect the accessory action of the table view cell with the navigation controller and set it present modally.
70. Add a new property for a ChecklistItem object below the other instance variables in AddItemViewController.swift
71. Add the viewDidLoad() method to AddItemViewController.swift
72. Change prepare(for:sender:) in ChecklistViewController.swift. (add edit item condition) (change 63)
73. Change viewDidLoad() in AddItemViewController.swift to fix the Done button
74. Add a new addItemViewController method (didFinishEditing) to the delegate protocol
75. In AddItemViewController.swift, change the done() method
Bug: “Method … has different argument names from those required by protocol …”.
76. Add the didFinishEditing to ChecklistViewController.swift
Bug: “Cannot invoke index with an argument list of type blah blah blah”.
77. Add the NSObject to the checklistItem class.
78. Replace the name of the addItemView controller with ItemDetailViewController
79. Add documentDirectory methods to ChecklistViewController
80. Add the saveChecklistItems method to ChecklistViewController
func saveChecklistItems() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(items, forKey: "ChecklistItems")
archiver.finishEncoding()
data.write(to: dataFilePath(), atomically: true)
}
81. Add a call to saveChecklistItems() to the end of ItemDetailViewControllerDelegate methods inside ChecklistViewController
82. Let’s not forget the swipe-to-delete function and toggling the checkmark on a row on or off
Bug: [Checklists.ChecklistItem encodeWithCoder:]: unrecognized selector sent to instance 0x7f8d6af3aac0
83. Add NSCoding to the class line in ChecklistItem, telling the compiler that ChecklistItem will conform to a new protocol, NSCoding. (need to add the 2 func in checklistItem)
84. Add the encode(with aCoder: NSCoder) to ChecklistItem
85. Add the init?(coder aDecoder: NSCoder) method to ChecklistItem
86. Add an init() method that takes no parameters to ChecklistItem.
87. Change the empty init?(coder) method in ChecklistItem. (change 83), take objects from the NSCoder’s decoder object and put their values inside your own properties.
88. In ChecklistViewController.swift, replace init?(coder)
89. Also add the loadChecklistItems() method
90. Add a new Cocoa Touch Class template, Class: AllListsViewController, Subclass of: UITableViewController
91. Remove the numberOfSections(in) method
92. Implement the tableView(cellForRowAt) method to put some text into the cells and add a makeCell method.
93. Drag a new Table View Controller onto the canvas
94. Ctrl-drag from the very first navigation controller to this new table view controller, Relationship Segue - root view controller
95. Select the new table view controller and set its Class in the Identity inspector to AllListsViewController
96. Ctrl-drag from the yellow circle icon at the top of All Lists View Controller into the Checklist View Controller and create a Show segue, go to the Attributes inspector and give it the identifier ShowChecklist.
97. In AllListsViewController.swift, add the tableView(didSelectRowAt) method
98. Add a new file to the project based on the Cocoa Touch Class template. Name it Checklist and make it a subclass of NSObject
99. Give Checklist.swift a name property
100. Give AllListsViewController an array that will store these new Checklist objects
101. Go to Checklist.swift and add the new init method
102. Go back to AllListsViewController.swift and add init?(coder), for real this time:
103. Change the tableView(numberOfRowsInSection) method to return the number of objects in the new array
104. Change tableView(cellForRowAt) to fill in the cells for the rows
105. Add a new instance variable to ChecklistViewController.swift
106. Change the viewDidLoad() method in ChecklistViewController.swift
107. In AllListsViewController.swift, update tableView(didSelectRowAt)
108. Add the prepare(for:sender:) method to AllListsViewController.swift
109. Add a new file to the project, ListDetailViewController.swift. You can either use the Cocoa Touch Class template or the Swift File template for this.
110. Replace the contents of ListDetailViewController.swift
111. Add the viewDidLoad() method
112. Also add the viewWillAppear() method to pop up the keyboard
113. Add the action methods for the Cancel and Done buttons
114. Also make sure the user cannot select the table cell with the text field
115. And finally, add the text field delegate method that enables or disables the Done button depending on whether the text field is empty or not.
116. Drag a new Navigation Controller from the Object Library into the canvas and move it below the other view controllers.
117. Select the new Table View Controller, Change its class to ListDetailViewController
118. Change the navigation bar title from “Root View Controller” to Add Checklist.
119. Add Cancel and Done bar button items and hook them up to the action methods in the view controller. Also connect the Done button to the doneBarButton outlet and uncheck its Enabled option.
120. Change the table view to Static Cells, style Grouped. You only need one cell, so remove the bottom two.
121. Drop a new Text Field into the cell.
122. Ctrl-drag from the view controller to the Text Field and connect it to the textField outlet.
123. Ctrl-drag the other way around, from the Text Field back to the view controller, and choose delegate under Outlets.
124. Connect the text field’s Did End on Exit event to the done action on the view controller.
125. Go to the All Lists View Controller (the one titled “Checklists”) and drag a Bar Button Item into its navigation bar. Change it into an Add button.
126. Ctrl-drag from this new bar button to the navigation controller below to add a new Present Modally segue.
127. Click on the new segue and name it AddChecklist
128. Make the AllListsViewController the delegate for the ListDetailViewController
129. Add the tableView(accessoryButtonTappedForRowWith) method to AllListsViewController.swift.
130. Open the storyboard and select the navigation controller that points to List Detail View Controller. Go to the Identity inspector and into the field Storyboard ID type ListDetailNavigationController:
131. Creates a new, empty, array that can hold ChecklistItem objects and assigns it to the items instance variable.
132. Remove the items instance variable from ChecklistViewController.swift.
133. Anywhere it says items you change it to say checklist.items instead.
134. Delete the documentsDirectory(),dataFilePath() , saveChecklistItems(), loadChecklistItems() methods from ChecklistViewController.swift.
135. Remove the lines that call saveChecklistItems().
136. Also delete init?(coder) from ChecklistViewController.swift.
137. Add some fake data into the various Checklist objects so that you can test whether this new design actually works.
138. Add the documentsDirectory(),dataFilePath() , saveChecklistItems(), loadChecklistItems() methods to AllListsViewController.swift.
139. Gets rid of the test data you put there earlier and makes the loadChecklists() method do all the work.
140. Add the NSCoding protocol in Checklist.swift
141. Add the method which loads and saves the Checklist’s name and items properties.
142. Remove the old Checklists.plist file from the Simulator’s Documents folder.
143. Add saveData() method to AppDelegate.swift
144. Change the applicationDidEnterBackground() and applicationWillTerminate() methods to call saveData()
145. Add a new file to the project using the Swift File template. Save it as DataModel.swift
146. Defines the new DataModel object and gives it a lists property.
147. Cut the documentsDirectory(),dataFilePath() , saveChecklistItems(), loadChecklistItems() methods out of AllListsViewController.swift and paste them into DataModel.swift
148. Add an init() method to DataModel.swift, makes sure that, as soon as the DataModel object is created, it will attempt to load Checklists.plist.
149. Switch to AllListsViewController.swift and make the following changes:
➤ Remove the lists instance variable.
➤ Remove the init?(coder) method.
➤ Add a new dataModel instance variable
150. Everywhere the code for AllListsViewController says lists, replace this with dataModel.lists. You need to do this in the following methods:
• tableView(numberOfRowsInSection)
• tableView(cellForRowAt)
• tableView(didSelectRowAt)
• tableView(commit, forRowAt)
• tableView(accessoryButtonTappedForRowWith)
• listDetailViewController(didFinishAdding)
• listDetailViewController(didFinishEditing)
151. In AppDelegate.swift, add a new dataModel property
152. Simplify the saveData() method
153. Share the DataModel instance with AllListsViewController in the application(didFinishLaunchingWithOptions) method
154. In AllListsViewController.swift, change tableView(didSelectRowAt) to store the index of the selected row into UserDefaults under the key “ChecklistIndex”.
155. Add the delegate protocol to the class line in AllListsViewController.swift
156. Add the delegate method to the bottom of AllListsViewController.swift
157. Add the viewDidAppear() method to AllListsViewController.swift
158. Add the registerDefaults() method inside DataModel.swift
159. Change DataModel.swift’s init() to call loadChecklists() and registerDefaults() method
160. Let’s move all of the UserDefaults stuff into DataModel
161. Update the code in AllListsViewController.swift to use this new computed property
162. Change the if-statement in viewDidAppear()
163. Change the registerDefaults() method in DataModel.swift, add a new default setting to the registerDefaults() method. The key for this value is “FirstTime”.
164. Still in DataModel.swift, add a new handleFirstTime() method
165. Call this new method from DataModel’s init()
166. Add the countUncheckedItems() method to Checklist.swift
167. Go to AllListsViewController.swift and in makeCell(for) change style: .default to style: .subtitle.
168. Add the cell.detailTextLabel!.text = "\(checklist.countUncheckedItems()) Remaining" just before return cell in tableView(cellForRowAt).
169. Go to AllListsViewController.swift and add the viewWillAppear() method
170. Change the relevant code in tableView(cellForRowAt) to change the label to read “All Done!” when there are no more to-do items left to check. Answerc
171. Change method in AllListsViewController’s implementation of “didFinishAdding” and “didFinishEditing
172. Add the sortChecklists() method to DataModel.swift
173. You should also call sortChecklists() when the plist file is loaded
174. Add the images from this folder to the asset catalog.
175. Add the iconName property to Checklist.swift
176. Extend init?(coder) and encode(with) to respectively load and save this icon name in the Checklists.plist file
177. Update init(name) to Give all new checklists the “Appointments” icon
178. Change tableView(cellForRowAt) in AllListsViewController.swift to put the icon into the table view cell:
179. Remove the Checklists.plist file or reset the Simulator
180. Change Checklist’s init(name) to give each Checklist object an icon named “No Icon” by default
181. Add a new Swift file to the project. Name it IconPickerViewController, defines the IconPickerViewController object
182. Add a constant (inside the class brackets) to hold the array of icons
183. Implement the data source methods: (numberOfRowsInSection, cellForRowAt ) for the table view.
184. Open the storyboard. Drag a new Table View Controller from the Object Library and place it next to the List Detail View Controller
185. In the Identity inspector, change the class of this new table view controller to IconPickerViewController.
186. Select the prototype cell and set its Style to Basic and its (re-use) Identifier to IconCell.
187. Go to the List Detail View Controller and add a new section to the table view. You can do this by changing the Sections field in the Attributes inspector for the table view from 1 to 2.
188. Delete the Text Field from the new cell;
189. Add a Label to this cell and name it Icon.
190. Set the cell’s Accessory to Disclosure Indicator.
191. Add an Image View to the right of the cell. Make it 36 × 36 points big. (Tip: use the Size inspector for this.)
192. Use the Assistant Editor to add an outlet property for this image view to ListDetailViewController.swift and name it iconImageView.
193. Ctrl-drag from the “Icon” table view cell to the Icon Picker View Controller and add a segue of type Selection Segue – Show.
194. Give the segue the identifier PickIcon.
195. Double-click that navigation bar and change its title to Choose Icon.
196. In ListDetailViewController.swift, change the “willSelectRowAt” table view delegate method
197. Add an instance variable in ListDetailViewController.swift to keep track of the chosen icon name
198. Update viewDidLoad() to copy the Checklist object’s icon name into the iconName instance variable.
199. Implement prepare(for:sender:) to ListDetailViewController.swift in order to tell the IconPickerViewController that this screen is now its delegate
200. Made the view controller conform to the delegate protocol
201. Add the implementation of the method from that delegate protocol somewhere inside the ListDetailViewController class:
202. Change the done() action so that it puts the chosen icon name into the Checklist object when the user closes the screen
203. Change IconPickerViewController to actually call the delegate method when a row is tapped.
204. Add a new init method to Checklist.swift that takes two parameters: name and iconName.
205. Making init(name) call init(name, iconName) with "No Icon" as the value for the iconName parameter.
206. Open the storyboard and go to the File inspector, Click Global Tint to open the color picker and choose Red: 4, Green: 169, Blue: 235.
207. Add the label.textColor = view.tintColor line to configureCheckmark(for:with:) in ChecklistViewController.swift
208. Open the Project Settings screen. In the General tab, scroll down to the App Icons and Launch Images section.
209. In the Launch Screen File box, press the arrow and select Main.storyboard.
210. Delete LaunchScreen.storyboard from the project.
211. From the Product menu choose Clean.
212. Select the Icon Image View. Bring up the Pin menu using the icon at the bottom of the canvas, uncheck Constrain to margins, Activate the bars at the top and the right so they turn red, Put checkmarks in front of Width and Height, For Update Frames choose Items of New Constraints, Finally, click Add 4 Constraints to finish.
213. Select the Text Field and in the Pin menu activate the four bars so they all become red
214. Also do this for the text field on the Add/Edit Item screen.
215. In the storyboard, go to the Checklist screen and select the label inside the prototype cell.
216. First use Editor → Size to Fit Content to give the label its ideal size.
217. Open the Pin menu and uncheck Constrain to margins, Activate the red bar on the right. Give it the value 0 so there is no spacing between the label and the disclosure button, set Update Frames to Items of New Constraints. Click Add 1 Constraint to add the new constraint.
218. With the label still selected, open the Align menu (next to Pin). Check Vertically in Container. Update Frames should be Items of New Constraints.
219. Select the label again. Ctrl-drag from the label to anywhere within the cell, select Leading Space to Container Margin from the popup.
220. Select the blue bar. In the Size inspector, change Constant to 30.
221. Open AppDelegate.swift and add a new UserNotifications import to the top of the file
222. Add the following to the method application(didFinishLaunchingWithOptions), just before the return true line:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) {
granted, error in
if granted {
print("We have permission") }
else {
print("Permission denied")
}
}
223. Stop the app and add the UNMutableNotificationContent code to didFinishLaunchingWithOptions
224. Add the UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate to AppDelegate’s class declaration
225. Also add the userNotificationCenter method to AppDelegate.swift
226. Finally, tell the UNUserNotificationCenter that AppDelegate is now its delegate. You do this in application(didFinishLaunchingWithOptions)
227. Remove the the local notification code from didFinishLaunchingWithOptions, but keep these lines:
let center = UNUserNotificationCenter.current()
center.delegate = self
228. Extend init?(coder) and encode(with) in order to be able to load and save these new properties along with the ChecklistItem objects
229. Asks the DataModel object for a new item ID whenever the app creates a new ChecklistItem object.
230 Add this new nextChecklistItemID() method to DataModel.
231. Hop on over to DataModel.swift and add nextChecklistItemID() method
232. Add a default value for “ChecklistItemID” to the registerDefaults() method
233. In ChecklistViewController.swift, change the configureText(for:with:)
234. Before you run the app, make sure to reset the Simulator first or throw away Checklists.plist from the app’s Documents directory.
235. Add the shouldRemindSwitch and dueDateLabel outlets to ItemDetailViewController.swift
236. Open the storyboard and select the Table View in the Item Detail View Controller, Add a new section to the table.
237. Remove the Text Field from the new cell. Drag a new Table View Cell from the Object Library and drop it below this one, so that the second section has two rows.
238. Add a Label to the first cell and give it the text Remind Me. Set the font to System, size 17.
239. Also drag a Switch control into the cell. Hook it up to the shouldRemindSwitch outlet on the view controller. In the Attributes inspector, set its Value to Off so it is no longer green.
240. Pin the Switch to the top and right edges of the table view cell.
241. simply set the Style of the cell to Right Detail and rename Title to Due Date.
242. The label on the right should be hooked up to the dueDateLabel outlet.
243. Add a new dueDate instance variable to ItemDetailViewController.swift
244. Change viewDidLoad()
245. The updateDueDateLabel() method is new. Add it to the file
246. The last thing to change in this file is the done() action.
247. Run the app and change the position of the switch control. The app will remember this setting when you terminate it
248. Add a new instance variable to ItemDetailViewController.swift, to keep track of whether the date picker is currently visible And add the showDatePicker() method
249. Open the storyboard and go to the Add Item scene. From the Object Library, pick up a new Table View Cell. Don’t drag it into the view controller itself but into the scene dock at the top
250. Select the Table View Cell and in the Size inspector set the Height to 217. The date picker is 216 points tall, plus one point for the separator line at the bottom of the cell.
251. In the Attributes inspector, set Selection to None so this cell won’t turn gray when you tap on it.
252. From the Object Library, drag a Date Picker into the cell. It should fit exactly
253. Use the Pin menu to glue the Date Picker to the four sides of the cell. Turn off Constrain to margins and then select the four bars to make them red (they all should be 0).
254. Make two new outlets and connect them to the cell and the date picker, respectively.
255. Add the tableView(cellForRowAt) method to ItemDetailViewController.swift
256. Override tableView(numberOfRowsInSection)
257. you also need to provide the tableView(heightForRowAt) method
258. Add didSelectRowAt method calls showDatePicker() when the index-path indicates that the Due Date row was tapped
259. Change tableView(willSelectRowAt)
260. Add the tableView(indentationLevelForRowAt) method
261. Add the dateChanged() method to ItemDetailViewController.swift
262. In the storyboard, Ctrl-drag from the Date Picker to the view controller and select the dateChanged: action method. Now everything is properly hooked up.
263. Add the datePicker.setDate(dueDate, animated: false) line to the bottom of showDatePicker()
264. Change showDatePicker() to sets the textColor of the detailTextLabel to the tint color.
265. Add the new hideDatePicker() method
266. Change tableView(didSelectRowAt) to toggle between the visible and hidden states
267. Add the textFieldDidBeginEditing() method
268. Add the scheduleNotification() method to ChecklistItem.swift
269. In the done() action in ItemDetailViewController.swift, add the item.scheduleNotification() line just before the call to didFinishEditing and also before `didFinishaAdding
270. In ChecklistItem.swift, change scheduleNotification()
271. To tell ChecklistItem about the User Notifications framework, you need to import UserNotifications to the top of the file, below the other import
272. Add the shouldRemindToggled method to ItemDetailViewController.swift
273. Also add an import UserNotifications or the above method won’t compile
274. Open the storyboard and connect the shouldRemindToggled: action to the switch control.
275. Add the removeNotification() method to ChecklistItem.swift
276. Call this new method from to the top of scheduleNotification()
277. Add the removeNotification() to the bottom of ChecklistItem.swift