這是***【總結(jié)回顧】iOS Apprentice Tutorial 2:Checklists ***系列的第七篇文章,也是最后一篇文章了拯爽,前幾篇文章請見(一) 赵讯、(二)、(三)痛阻、(四)、(五)腮敌、(六)阱当。
本篇文章總結(jié)本書的第章( Improving the user experience、Extra feature: local notifications)中的重點內(nèi)容糜工,主要是講述如何實現(xiàn)一些提升用戶體驗的功能弊添,例如記錄每個清單里未完成項目的數(shù)量和清單中項目總數(shù),每次添加新的清單后能夠給清單自動排序捌木,可給清單增加小圖標(biāo)油坝,讓界面更好看,以及適配所有的機型。從205頁到269頁(最后一頁)澈圈。
67. 優(yōu)化用戶體驗之顯示未完成數(shù)量
func countUncheckedItems() -> Int {
var count = 0
for item in items where !item.checked {
count += 1
}
return count
}
此方法返回值就是此清單中沒有完成的數(shù)量彬檀。當(dāng)然此方法也可以有另外一種寫法:
func countUncheckedItems() -> Int {
var count = 0
for item in items {
if !item.checked {
count += 1
}
}
return count
}
然后在AllListsViewController.swift中加入下列方法,保持顯示數(shù)據(jù)的同步:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
當(dāng)然接下來還可以實現(xiàn)一些小功能瞬女,比如全部完成的時候文案顯示全部完成凤覆,清單里還沒有添加項目的時候顯示沒有項目等等。
68. 優(yōu)化用戶體驗之自動排序
func sortChecklists() {
lists.sortInPlace({ checklist1, checklist2 in
return checklist1.name.localizedStandardCompare(checklist2.name) == .OrderedAscending
})
}
這個方法的好處是拆魏,如果用戶是用中文的,排序按照拼音的順序排序慈俯,如果是英文渤刃,則按照英文從a到z的順序來。用戶使用的語言不對贴膘,相應(yīng)的順序也是不一樣的卖子。
真正的排序公式是:
checklist1.name.localizedStandardCompare(checklist2.name) == .OrderedAscending
如果你想按其他方式排序,只需要改動這一行代碼即可刑峡。
當(dāng)然洋闽,還要在下載讀取plist文件(也就是在loadChecklists()
方法里)的時候,調(diào)用這一方法
69. 優(yōu)化用戶體驗之增加選擇圖片
允許用戶給每個list選擇一個圖標(biāo)突梦,實際效果如下:
當(dāng)然了诫舅,創(chuàng)建編輯list的時候,也需要增加圖標(biāo)這個選項:
將設(shè)計好的圖片放入Xcode宫患,在 Checklist.swift 文件里增加iconName變量刊懈,聲明,初始化娃闲,NSCdoing 的兩個協(xié)議方法虚汛,都要加上。然后到AllListsViewController皇帮、ListDetailViewController里把對應(yīng)的圖片顯示出來(一個是所有清單列表的界面卷哩,一個是增加或者編輯清單的界面)。在增加或編輯清單界面中點擊第二行cell属拾,跳轉(zhuǎn)到之后選擇圖標(biāo)界面将谊,Segue的Identifier是“PickIcon”,這個界面(IconPickerViewController.swift)需要先創(chuàng)建捌年。
當(dāng)用戶沒有選擇圖標(biāo)時瓢娜,實際上顯示的是一張完全透明的圖片,這樣的效果就是下圖中右側(cè)的情況:
新建選擇圖片的swift文件:IconPickerViewController.swift礼预,storyboard中拖入一個tableview Controller眠砾,關(guān)聯(lián),代碼如下。
import UIKit
protocol IconPickerViewControllerDelegate: class {
func iconPicker(picker: IconPickerViewController, didPickIcon iconName: String)
}
class IconPickerViewController: UITableViewController {
weak var delegate: IconPickerViewControllerDelegate?
let icons = [
"No Icon",
"Appointments",
"Birthdays",
"Chores",
"Drinks",
"Folder",
"Groceries",
"Inbox",
"Photos",
"Trips" ]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return icons.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("IconCell", forIndexPath: indexPath)
let iconName = icons[indexPath.row]
cell.textLabel!.text = iconName
cell.imageView!.image = UIImage(named: iconName)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let delegate = delegate {
let iconName = icons[indexPath.row]
delegate.iconPicker(self, didPickIcon: iconName)
}
}
}
然后回到增加或者編輯清單界面的view controller里補上IconPickerViewController.swift里創(chuàng)建的delegate的有關(guān)的代碼褒颈,別忘了在類的開頭寫上:IconPickerViewControllerDelegate
柒巫。
下面代碼都是因為增加了圖標(biāo)而做出了相應(yīng)修改的代碼:
override func viewDidLoad() {
super.viewDidLoad()
if let checklist = checklistToEdit {
title = "Edit Checklist"
textField.text = checklist.name
doneBarButton.enabled = true
iconName = checklist.iconName
}
iconImageView.image = UIImage(named: iconName)
}
@IBAction func done() {
if let checklist = checklistToEdit {
checklist.name = textField.text!
checklist.iconName = iconName
delegate?.listDetailViewController(self, didFinishEditingChecklist: checklist)
} else {
let checklist = Checklist(name: textField.text!, iconName: iconName)
delegate?.listDetailViewController(self, didFinishAddingChecklist: checklist)
}
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
if indexPath.section == 1 {
return indexPath
} else {
return nil
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PickIcon" {
let controller = segue.destinationViewController as! IconPickerViewController
controller.delegate = self
}
}
func iconPicker(picker: IconPickerViewController, didPickIcon iconName: String) {
self.iconName = iconName
iconImageView.image = UIImage(named: iconName)
navigationController?.popViewControllerAnimated(true)
}
}
上面的代碼有好多行,貌似是一項巨大工程谷丸,其實主要是設(shè)計的方法有些多堡掏,所以容易落下某個代碼沒有改∨偬郏總結(jié)起來泉唁,就三件事情:
- 增加一個新的 view controller 對象。
- 在Storyboard中設(shè)計界面(還需要做一些自動布局約束的工作)
- 然后用 segue 和 delegate 將這個新創(chuàng)建的 view controller 對象連接到 增加編輯清單 界面揩慕。
70. 優(yōu)化用戶體驗之優(yōu)化界面+適配機型
作者在優(yōu)化外表的時候走了一個快捷方式亭畜,使用 tint color。tint color 能改變哪些地方呢迎卤?見下圖:
而且更改 tint color 的方法非常簡單拴鸵,無需在Storyboard中一個一個界面的改,只要改掉 Global Tint 即可蜗搔。如下圖:
這樣全局的 tint color 都是你想要的顏色啦多簡單劲藐,一步到位
還有一個改進的地方:啟動的時候,顯示App的一部分樟凄,這樣給用戶一種錯覺聘芜,App很快就啟動起來了。其實現(xiàn)國外的App這樣的設(shè)計比較多不同,國內(nèi)的App都不這樣做了厉膀,都會弄個廣告啊,或者圖片啊什么的二拐。不過服鹅,也了解一下如何實現(xiàn)吧:
至于書中說的適配所有機型,其實只修改了 TextField 控件百新,只要這個控件在不同的機型下顯示有些問題企软。
71. 函數(shù)式編程
近年來,函數(shù)式編程日趨流行饭望。使用函數(shù)式編程有好處仗哨,可以縮短代碼量。不過對于新手來說铅辞,可能閱讀代碼的時候會有些不習(xí)慣厌漂,不過慢慢習(xí)慣了就好了。
比如:
func countUncheckedItems() -> Int {
var count = 0
for item in items where !item.checked {
count += 1
}
return count
}
用函數(shù)式編程寫出來就是
func countUncheckedItems() -> Int {
return items.reduce(0) { cnt, item in cnt + (item.checked ? 0 : 1) }
}
reduce()
這個方法斟珊,每次看到一個item就執(zhí)行一遍{}里的代碼苇倡。cnt變量一開始的值是0,每次根據(jù)item的情況來加1或0.
72. Convenience Initializer
convenience init(name: String) {
self.init(name: name, iconName: "No Icon")
}
init(name: String, iconName: String) {
self.name = name
self.iconName = iconName
super.init()
}
好吧,這個地方我實際上沒太看懂旨椒,先復(fù)制一下原文晓褪,等哪天理解了,再總結(jié)一下综慎。
Instead of
super.init()
it now callsself.init(name, iconName)
. Because it farms out its work to another init method,init(name)
is now known as a convenience initializer. It does the same thing asinit(name, iconName)
but saves you from having to type iconName: "No Icon" whenever you want to use it.
init(name, iconName)
has become the so-called designated initializer for Checklist. It is the primary way to create new Checklist objects, whileinit(name)
exists only for the convenience of lazy developers... such as you and me. :-)
73. 本地提醒功能(local notifications)
首先要了解的一點是涣仿,這是 local notifications,不是開發(fā)時常常用到的 push notifications示惊,push notifications可以讓你的 App 接收外部的事件好港,比如新聞推送某只球隊進了世界杯。
local notifications 更有點像是鬧鐘米罚,用戶在使用 App 的時候媚狰,設(shè)置了一個時間點,到點就會提醒阔拳。提醒的前提是,應(yīng)用已經(jīng)在后臺運行类嗤,或者應(yīng)用沒有啟動糊肠,如果應(yīng)用正在使用,本地通知是不會顯示的遗锣。這時候需要用其他方法另作處理货裹。
在 iOS8 之后,只有在獲得用戶的允許后才能發(fā)送 local notifications精偿,如果用戶拒絕弧圆,到點也不會出現(xiàn)提醒信息。獲取用戶許可的方法在后面會說笔咽。
(1)擴展 data model
增加了提醒意味著數(shù)據(jù)也要增加相對應(yīng)的屬性搔预,因為每個清單里的一個item里有提醒,所以要做ChecklistItem文件里增加屬性叶组。不過 UILocalNotification對象是無法存入plist文件中的拯田,需要找替代方案,比如數(shù)字標(biāo)識符:numeric primary甩十。
var dueDate = NSDate()
var shouldRemind = false
var itemID: Int
接著在 NSCoding 的2個協(xié)議方法增加相應(yīng)的代碼船庇。在Objective-C語言中,Int侣监,F(xiàn)loat和Bool是原始類型鸭轮,所以會看到shouldRemind
、checked
和itemID
的方法有別與非原始類型的對象橄霉。
required init?(coder aDecoder: NSCoder) {
text = aDecoder.decodeObjectForKey("Text") as! String
checked = aDecoder.decodeBoolForKey("Checked")
dueDate = aDecoder.decodeObjectForKey("DueDate") as! NSDate
shouldRemind = aDecoder.decodeBoolForKey("ShouldRemind")
itemID = aDecoder.decodeIntegerForKey("ItemID")
super.init()
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(text, forKey: "Text")
aCoder.encodeBool(checked, forKey: "Checked")
aCoder.encodeObject(dueDate, forKey: "DueDate")
aCoder.encodeBool(shouldRemind, forKey: "ShouldRemind")
aCoder.encodeInteger(itemID, forKey: "ItemID")
}
不要忘了更新初始化方法窃爷,注意代碼中DataModel是大寫開頭的。
override init() {
//注意這里是大寫開頭的DataModel
itemID = DataModel.nextChecklistItemID()
super.init()
}
在初始化方法里寫這么一行代碼是什么意思呢?不管什么時候只要App創(chuàng)建了一個新的 ChecklistItem 對象后吞鸭,都讓 DataModel 對象生成一個新的item ID寺董。
之所以可以大寫開頭,是因為我們要在DataModel類中創(chuàng)建一個類方法(方法前面有個class刻剥,詳細(xì)內(nèi)容見#74):
class func nextChecklistItemID() -> Int {
let userDefaults = NSUserDefaults.standardUserDefaults()
//讀取出來值
let itemID = userDefaults.integerForKey("ChecklistItemID")
//寫入新值
userDefaults.setInteger(itemID + 1, forKey: "ChecklistItemID")
//保存同步
userDefaults.synchronize()
return itemID
}
NSUserDefaults里面沒有ChecklistItemID這個鍵啊遮咖,是的,沒有造虏,所以需要加入:
func registerDefaults() {
let dictionary = [ "ChecklistIndex": -1,
"FirstTime": true,
"ChecklistItemID": 0 ]
NSUserDefaults.standardUserDefaults().registerDefaults(dictionary)
}
(2)搭建界面
先上效果圖:
這個直接去控件庫里拖動相對應(yīng)的控件即可御吞,沒啥可說的,別忘了自動布局的約束漓藕。而下圖這個就有些問題了陶珠,作者提供的方法非常新穎,我使用之后發(fā)現(xiàn)有個不好的地方享钞,就是擴展性不夠好揍诽,沒法擴展到多個時間選擇器上,比如這里只要提醒時間栗竖,要是我再加入開始時間和結(jié)束時間暑脆,不好實現(xiàn)同樣的效果,擴展性不夠狐肢。
步驟如下:
步驟一添吗,把cell拖到下圖中箭頭所在的位置:
拖拽之后會出現(xiàn)這樣的效果:
cell高度設(shè)為217,拖入一個DatePicker控件份名,效果如下:
(3)對應(yīng)的Controller里編寫代碼:
還要創(chuàng)建Outlet連接:
@IBOutlet weak var shouldRemindSwitch: UISwitch!
@IBOutlet weak var dueDateLabel: UILabel!
@IBOutlet weak var datePickerCell: UITableViewCell!
@IBOutlet weak var datePicker: UIDatePicker!
聲明新的變量:
var dueDate = NSDate()
var datePickerVisible = false
更新viewDidLoad()
中的方法:編輯狀態(tài)下2個屬性要加入+更新提醒日期的Label內(nèi)容(updateDueLabel()
):
override func viewDidLoad() {
super.viewDidLoad()
if let item = itemToEdit {
title = "Edit Item"
textField.text = item.text
doneBarButton.enabled = true
//新增加的兩行代碼:
shouldRemindSwitch.on = item.shouldRemind
dueDate = item.dueDate
}
//需要創(chuàng)建的方法:
updateDueDateLabel()
}
updateDueLabel()
方法代碼:
func updateDueDateLabel() {
let formatter = NSDateFormatter()
formatter.dateStyle = .MediumStyle
formatter.timeStyle = .ShortStyle
dueDateLabel.text = formatter.stringFromDate(dueDate)
}
NSDateFormatter可以改變時間顯示的格式碟联。
@IBAction func done()
方法里也要增加新增的2個屬性。
下面的代碼都是有關(guān)如何顯示時間選擇器cell的:
看完這些方法僵腺,你就會理解我為什么說作者提供的這個方法非常麻煩了鲤孵。
- cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 1 && indexPath.row == 2 {
return datePickerCell
} else {
return super.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
}
2)numberOfRowsInSection
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 1 && datePickerVisible {
return 3
} else {
return super.tableView(tableView, numberOfRowsInSection: section)
}
}
3)heightForRowAtIndexPath
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 1 && indexPath.row == 2 {
return 217
} else {
return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}
}
4)didSelectRowAtIndexPath
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
textField.resignFirstResponder()
if indexPath.section == 1 && indexPath.row == 1 {
if !datePickerVisible {
showDatePicker()
} else {
hideDatePicker()
}
}
}
5) willSelectRowAtIndexPath
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
if indexPath.section == 1 && indexPath.row == 1 {
return indexPath
} else {
return nil
}
}
6)indentationLevelForRowAtIndexPath
override func tableView(tableView: UITableView, var indentationLevelForRowAtIndexPath indexPath: NSIndexPath) -> Int {
if indexPath.section == 1 && indexPath.row == 2 {
indexPath = NSIndexPath(forRow: 0, inSection: indexPath.section)
}
return super.tableView(tableView, indentationLevelForRowAtIndexPath: indexPath)
}
到此結(jié)束了。但這樣只能讓cell一直出現(xiàn)辰如,時間選擇器會一直在這里出現(xiàn)的裤纹,這可不是我們想要的效果。我們需要點擊一下顯示丧没,再點擊一下隱藏鹰椒,那么好吧,繼續(xù)寫代碼吧~
下面這些代碼有關(guān)顯示隱藏時間選擇器
1)顯示時間選擇器
func showDatePicker() {
datePickerVisible = true
let indexPathDateRow = NSIndexPath(forRow: 1, inSection: 1)
let indexPathDatePicker = NSIndexPath(forRow: 2, inSection: 1)
if let dateCell = tableView.cellForRowAtIndexPath(indexPathDateRow) {
dateCell.detailTextLabel!.textColor = dateCell.detailTextLabel!.tintColor
}
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([indexPathDatePicker], withRowAnimation: .Fade)
tableView.reloadRowsAtIndexPaths([indexPathDateRow], withRowAnimation: .None)
tableView.endUpdates()
datePicker.setDate(dueDate, animated: false)
}
2)隱藏時間選擇器
func hideDatePicker() {
if datePickerVisible {
datePickerVisible = false
let indexPathDateRow = NSIndexPath(forRow: 1, inSection: 1)
let indexPathDatePicker = NSIndexPath(forRow: 2, inSection: 1)
if let cell = tableView.cellForRowAtIndexPath(indexPathDateRow) {
cell.detailTextLabel!.textColor = UIColor(white: 0, alpha: 0.5)
}
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPathDateRow], withRowAnimation: .None)
tableView.deleteRowsAtIndexPaths([indexPathDatePicker], withRowAnimation: .Fade)
tableView.endUpdates()
}
}
優(yōu)化用戶體驗
啟動鍵盤后要隱藏日期選擇器:
func textFieldDidBeginEditing(textField: UITextField) {
hideDatePicker()
}
日期選擇器中的時間每次發(fā)生改變后都更新Label內(nèi)容:
@IBAction func dateChanged(datePicker: UIDatePicker) {
dueDate = datePicker.date
updateDueDateLabel()
}
(4)如何以及什么時候安排通知(schedule the notifications)
好吧呕童,終于開始寫有關(guān)通知的代碼了漆际,上面那么多東西,都是鋪墊鋪墊鋪墊夺饲。奸汇。施符。
先創(chuàng)建一個方法,用來創(chuàng)建通知:
func scheduleNotification() {
}
這個方法寫入ChecklistItem.swift這個文件里擂找。
關(guān)于通知的時間戳吝,要注意,過去的時間點是無法提醒的贯涎,但是用戶設(shè)置了過去的時間怎么辦呢听哭?
dueDate.compare(NSDate()) = .OrderedAscending
.OrderedAscending
表示 dueDate 的時間在前,現(xiàn)在時間NSDate()在后塘雳,也就是說陆盘,提醒時間發(fā)生在過去。只有將時間設(shè)置到將來才可以提醒败明,所以設(shè)置提醒的條件為:!=隘马,當(dāng)然了,還要用戶開啟提醒功能:
if shouldRemind && dueDate.compare(NSDate()) != .OrderedAscending {
}
補充一下妻顶,NSComparisonResult 的結(jié)果酸员,也就是 A.compare(B) 結(jié)果有三種:
- .OrderedAscending,也是我們正在使用的讳嘱,A發(fā)生在B之前(A在過去沸呐,B在將來)。
- .OrderedSame呢燥,表示兩個時間一致,A和B時間是同一個時間點寓娩。
- .OrderedDescending叛氨,表示A發(fā)生在B之后,也就是先經(jīng)過B時間點棘伴,然后才是A時間點寞埠。
當(dāng)然了,每創(chuàng)建一個新的 Item 或者編輯一個 Item 的時候焊夸,都要檢查一下是否要創(chuàng)建通知仁连,所以:
@IBAction func done() {
if let item = itemToEdit {
item.text = textField.text!
item.shouldRemind = shouldRemindSwitch.on
item.dueDate = dueDate
item.scheduleNotification()
delegate?.itemDetailViewController(self, didFinishEditingItem: item)
} else {
let item = ChecklistItem()
item.text = textField.text!
item.checked = false
item.shouldRemind = shouldRemindSwitch.on
item.dueDate = dueDate
item.scheduleNotification()
delegate?.itemDetailViewController(self, didFinishAddingItem: item)
}
}
現(xiàn)在可以創(chuàng)建通知了,創(chuàng)建本地通知有7行非常關(guān)鍵的代碼:
- 創(chuàng)建本地通知
- 創(chuàng)建提醒時間
- 創(chuàng)建時間提醒的時區(qū)
- 創(chuàng)建通知顯示的文案或內(nèi)容
- 創(chuàng)建通知使用的聲音
- 通知提醒的是哪個內(nèi)容(哪個item)
- 將創(chuàng)建好的本地通知排入時間表
用代碼表示也就是:
let localNotification = UILocalNotification()
localNotification.fireDate = dueDate
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.alertBody = text
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.userInfo = ["ItemID": itemID]
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
注意第一行代碼我們實際上創(chuàng)建的是一個UILocalNotification對象阱穗,要使用這個對象饭冬,需要先 import UIKit。
當(dāng)然我們要獲取用戶的許可才能設(shè)置通知揪阶,處理方法就是昌抠,當(dāng)用戶將switch按鈕調(diào)整到on的時候,向用戶獲取許可鲁僚,Ctrl拖拽創(chuàng)建Action連接:
@IBAction func shouldRemindToggled(sender: UISwitch) {
//隱藏鍵盤
textField.resignFirstResponder()
if sender.on {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert , .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
}
}
當(dāng)我們設(shè)置時間的時炊苫,會記錄到秒裁厅,比如提醒時間是10:16:54,實際上我們在設(shè)置時間的時候侨艾,時間選擇器里只顯示到了分执虹,但是提醒的時候,卻會連秒也考慮到里面唠梨。如果想提高用戶體驗袋励,去掉秒,直接在0秒的時候就提醒姻成,那么插龄,這是另外一個話題了。如何解決呢科展?作者也沒說均牢。
通知可能發(fā)生變化的五種情形:
- 當(dāng)用戶新建一個 ChecklistItem 對象后,將 ShouldRemind 轉(zhuǎn)換按鈕調(diào)整到了開上才睹,需要安排一個新的通知(相當(dāng)于新建通知)徘跪。
- 當(dāng)用戶改變提醒日期后,舊的通知需要取消琅攘,然后更換上新的通知日期垮庐。
- 當(dāng)用戶將 ShouldRemind 轉(zhuǎn)換按鈕調(diào)整到關(guān)的狀態(tài)后,當(dāng)前的通知需要取消掉坞琴。
- 當(dāng)用戶刪除當(dāng)前的 ChecklistItem 后哨查,該Item下的通知需要被取消。
- 當(dāng)用戶刪除一整個 Checklist 后剧辐,里面所有的Item下的通知都需要被取消寒亥。
我們上面的各種步驟已經(jīng)完成了新建通知,那么編輯和刪除如何進行呢荧关?
先說編輯溉奕,當(dāng)我們編輯一個item的時候,先看是否存在一個通知忍啤,如果存在加勤,取消通知即可,然后新建同波。每次編輯鳄梅,都相當(dāng)于刪除舊的,重新創(chuàng)建新的通知未檩,如果用戶在編輯的時候沒有修改通知卫枝,頂多就是新建了一個和之前一模一樣的通知而已。
func notificationForThisItem() -> UILocalNotification? {
let allNotifications = UIApplication.sharedApplication().scheduledLocalNotifications!
for notification in allNotifications {
if let number = notification.userInfo?["ItemID"] as? Int where number == itemID {
return notification
}
}
return nil
}
判斷此item是否有通知讹挎。然后放入創(chuàng)建通知的方法里:
func scheduleNotification() {
let existingNotification = notificationForThisItem()
if let notification = existingNotification {
UIApplication.sharedApplication().cancelLocalNotification(notification)
}
if shouldRemind && dueDate.compare(NSDate()) != .OrderedAscending {
let localNotification = UILocalNotification()
localNotification.fireDate = dueDate
localNotification.timeZone = NSTimeZone.defaultTimeZone()
localNotification.alertBody = text
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.userInfo = ["ItemID": itemID]
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
}
最后剩下的就是刪除了校赤,即通知可能發(fā)生變化的五種情形的最后兩種情緒吆玖,用一個方法即可解決:
deinit {
if let notification = notificationForThisItem() {
UIApplication.sharedApplication().cancelLocalNotification(notification)
}
}
當(dāng)年刪除單個的ChecklistItem或者一個整個Checklist時,都會調(diào)用上面方法马篮。
74. Class method vs. instance method(類方法和實例方法)
class func nextChecklistItemID()
class 這個關(guān)鍵詞意味著你可以直接調(diào)用該方法沾乘,不用創(chuàng)建一個 DataMode 的對象的引用。
之前用的都是 instance method浑测,只使用于類中某些實例翅阵。那什么時候用類方法什么時候用引用方法呢?哪個方法用的代碼更少就用哪個方法啦迁央,畢竟在這個App里掷匠,就這么一個地方用到了DataModel,如果以后你需要更新App岖圈,增加更多功能讹语,很有可能就需要使用創(chuàng)建引用類型,然后使用引用方法蜂科。
更多詳細(xì)的介紹會在下一本書中講解顽决,所以留著點疑問去看下一本書吧~
結(jié)束了
終于總結(jié)完畢了,終于看完了导匣,本來以為一個周就能搞定才菠,結(jié)果來來回回拖了這么久才完成,我也是小瞧了這本書贡定,本來以為是100米短跑赋访,跑完5000米之后才發(fā)現(xiàn),前面還有一個馬拉松缓待。蚓耽。。這就是我總結(jié)這本書的感受命斧,再也不敢隨意估算時間了,以后自己估計時間了之后再乘以3嘱兼,如果完全沒做過国葬,乘以7,就是實際時間了芹壕。
照著書敲三遍代碼汇四,和總結(jié)一遍知識點,完全不是一個量級上的工作踢涌,雖然中間數(shù)次想放棄通孽,好歹也走到了今天。有了這本書的知識睁壁,我完全可以開發(fā)一個無網(wǎng)絡(luò)交互的本地App了背苦,最起碼數(shù)據(jù)持久化可以搞定了互捌。要是哪天App上線了,我要在這里更新行剂,多說幾句~~
能看完的都是秕噪,我很佩服,總之厚宰,看到錯別字或者錯誤的知識點腌巾,還請指正~~