To-Do List App第三集:資料儲存、時間標記與使用者通知

這是延續之前的作品,繼續完成To-Do List的App。前兩集連結如下:

先上成果:

  • 設定待辦事項日期:

※在模擬器或實機中,只要地區設定為台灣,新版的Date Picker好像都會遇到如上圖那種顯示不完全的問題,疑似是iOS 14還沒解決的Bug

  • 設定時間並開啟通知
  • 通知跳出
  • 新增待辦事項
  • 資料儲存

資料儲存:User Defaults

User Default是比較初階的儲存方法,不過相較於其他方式,User Default的內容會持續佔用記憶體,所以在這裡只是練習而已,最後捨棄不用,程式碼如下:

資料儲存:Document Directory

這是最終採用的方法,原理是在App的檔案中新增一個檔案,紀錄好所需的資料,並讀取出來,這樣App關掉重開的話,資料也不會消失,就能保存住資料。程式碼如下:

時間處理

這次最大的更動,是讓待辦事項多了日期與時間,我設計的概念比較像iOS內建的提醒事項,也就是使用者可以僅設定日期、也可以設定日期與時間,但通知的部分一定要有設定時間才能開啟,並可以設定要多久之前通知。

因此,在資料的部分,struct裡面就要有兩個Date;而畫面中要呈現兩個不同的Date Picker,分別是「日期」跟「時間」,其中又要讓「時間」的Date的範圍跟著「日期」的Date移動,才能在選中的日期中的時間正確地跳出通知。

這部分折騰了蠻久,好像沒有一個內建的function可以取得同一時區中、同一日期的24小時,所以我自己寫了這一串,再導入「日期」的Date Picker的時間,去限縮「時間」的Date Picker 範圍:

簡單來說就是「日期」選中的Date,去減掉這個日期除以86400(等於24小時)的餘數,再減掉28800(時區調整),就是選中的日期當天的台灣時間凌晨0點,就能當作「時間」的Date Picker的最小值;最後再加上一天的86400,就能當作最大值,於是就限縮了「時間」這個Date值的範圍。

唯一的缺點就是無法跟著時區改變而調整,或是要更複雜地去定義每個時區的矯正值,如果有其他更好的方法,請大家不吝指教><

這次是我第一次深入處理Date這種資料形式,可以參考這篇文章,內容關於Date解釋地非常詳盡、全面。

UI部分

如上所述,在待辦事項的詳細資料頁面中,要讓使用者可以「僅選日期」、「選擇日期與時間」、「選擇關閉通知」,因此在這部分也花了蠻大的力氣,來處理各種Button、Label顯示與否的連動,礙於篇幅就不附上詳細的程式碼,成果如下:

這個詳細資料的頁面,是用Table View Controller + Static Cell完成的,一開始不知道怎麼下手,問了Peter之後,才知道蘋果官方的App Development with Swift中有很詳盡的範例,Peter有寫一篇文章,連結如下,可參考「4.9 Building Complex Input Screens」的部分:

從這份範例中,學到最大的突破應該是把IndexPath跟switch 結合,先定義好要調整的目標(例如要調整是否隱藏的TimeDatePicker那行)的indexPath,先存在一個常數中,然後在heightForRowAt中,用switch去設條件,就可以決定在什麼情況下,表格中哪些特定的行要凸顯或隱藏。我的程式碼如下:

除此之外,還有個小細節,就是範例中都是用beginUpdates與endUpdates,來重整表格,呈現出來的效果會比tableView.reloadData還要好!

第一頁基本待辦事項的表格中,也分別對「僅有日期」、「有日期與時間」做出不同的UI處理,成果如下:

另一方面,我的待辦事項有Notes(附註)的部分,也就會牽涉到TextView的使用,我參考了這篇文章來寫TextView編輯時,按空白處收鍵盤的function,並用這篇文章提供的解答,做出了TextView的Placeholder。

Navigation Controller相關

從第二頁待辦事項的詳細資料,傳送回第一頁基本的待辦事項表格,打算用unwind segue完成,結果遇到一個問題是,當初的navigation controller是用程式寫出來的,所以storyboard上就沒有顯示Navigation Item,變成也沒辦法去拉unwind segue。

求助Peter後,得到下面這篇文章,用Storyboard的Simulated Metrics去加上模擬的Navigation Bar,就可以做出想要的效果了!

另外,在實作的過程中,也有遇到一個問題:navigation bar會自動產生一個backBarButtonItem,但把那個backBarButtonItem加上action後卻完全無效。

後來找到這篇stack overflow,確實沒辦法把那個backBarButtonItem加上我們想要的action,必須先把navigationItem.hidesBackButton設成true,再另外寫出我們要的按鈕,才能加上我們要的action。

通知功能

Peter在AppCoda上的這篇文章,把使用者通知推播的功能解釋地非常清楚:

礙於篇幅就不另外貼上程式碼。寫出來之後,一開始跑下去就跳出使用者通知的權限要求,看到的當下真是無比感動…..

其他

  • 我設定Notification type的部分,是用enum做出來的,但在後續處理的過程中發現有點棘手,查了一下,發現果然有把enum當成Array來處理的方法:遵從 protocol CaseIterable。可以參考Peter這篇文章:
  • 在調整待辦事項的頁面中,要調整的東西很多很繁瑣,我透過上述提到的蘋果官方範例,是用computed property直接去整理出最終的TodoItem,就不用另外寫零碎的function去處理,程式碼如下,也變得乾淨簡潔很多!

Github

終於完成這個作品了!Github上的Tag為3.0,連結如下:

--

--