#121 利用CollectionView製作橫向日期選擇器(Horizontal Date Picker)

日前在練習Swift的時候,偶然在Pinterest上看到橫向的日期選擇器,那時候也覺得是個很有趣的元件。在Github上搜尋後發現沒人做過類似的元件供使用或參考,所以決定自己來刻一個。

首先先來談論此次使用到的概念

·UICollectionViewDelegate,DataSource,Cell,UICollectionFlowLayout(調整基礎樣式及設定CollectionView之代理)

·Date,DateFormatter(設定格式)

·Calendar(dateComponents,byAdding)(計算日期)

·ScrollToItem(調整被挑選項目之位置)

首先,建立起基礎的文件準備進行後續調整,建立起一個帶CollcetionView的ViewController,並將CollectionView設為Horizontal。以此告訴Swift,我接下來要做的是橫向的滑動視窗並取消Show Horizontal Indicator(滑動桿)。

完成後對畫面進行下一步的調整,設定Cell的identifier,並在中間設計想呈現的Date Picker呈現形式,並在CollectionView上方會顯示目前是幾年幾月,本次我希望是呈現上方是週一到週日的英文名稱,下方為阿拉伯數字的日期呈現。

因此實際設計出的畫面為下圖:

設定完畫面的部分後,就開始來處理程式的部分,首先先製作@IBOutlet的部分。

設定好之後,在ViewController配置上方年月部分的@IBOutlet,並接著要來處理一個問題

要怎麼控制一個畫面中要同時顯示幾個Cell出現?

為此會需要使用到UICollectionFlowLayout,為此會需要CollectionView的寬度,因此先宣告:let layout = collectionViews.collectionViewLayout as? UICollectionViewFlowLayout -> 先將CollectionView的寬度抓出,並設定estimate為.zero ->讓Cell維持同樣長寬,並設定間距minimumLineSpacing為0,讓Cell中間不出現空隙,

接著利用itemSize 配置出畫面中配置的Cell數量

完成的結果如下圖,若想知道更詳細的使用可參考彼得潘文章:這裏

處理好畫面部分,接著就可以來處理日期選擇的部分,首先我花了一陣子思考我到底該抓多大的Range才適合作為DatePicker的日期範圍(因為攸關需要告訴Swift我的CollectionView長度會是多長),所以目前我先抓約4 years的時間作為長度,若之後找到更好的辦法則再修改。

接著開始計算日期長度

如何能夠計算日期長度呢?此時可以利用Calender中的dateComponents函數,通過設定初始日期及結束日期,則可以計算出從開始到結束的長度。

其中[.day]為計算單位,可在其中輸入day,month,minute等各種時間單位來做為計算單位要求Swift協助計算,而後面StartDate及endDate則透過DataFormmatter宣告,最後透過dateComponents函數進行計算,最後回傳給CollectionView的numberOfItemsInSection知道整個DatePicker的長度會是多長。

完整func:

處理datePicker中的年月日顯示

這部分完成後,接著來處理CollectionViewCell中的年月日的顯示,首先會先通過dequeueReusableCell告知Swift,這些Cell是可以被重複使用的,並開始利用DateFormatter設計日期格式。

先通過Dateformatter宣告格式,接著透過Optional Binding使用Calender中的byAdding,通過

Calendar.current.date(byAdding: , value: , to: date,wrappingComponents: false)

來為每個Cell中放入有順序的日期,其中

byAdding : 時間單位 Ex: .day .month .hour 每個Cell間隔的時間單位

value : 單位為Int,設定每個時間單位的精準時間 Ex: indexPath.row-2 ,若今日為10/10號,則以.day為單位,indexPath.row假設為0,則Value = -2,故時間為10/10減掉2天,故為10/08號

wrappingComponents:時間超過當月是否跳入下一月份?若選擇True,則會在當月份中不斷循環,因此此處為false

我的完整做法:

接著將設計好的時間,轉化成String並放入Label.text當中則大功告成,到這邊已經可以有一個可滑動的橫向日期。

Point:Code中將月份及日期分開Optional Binding是因為日期及月份會有不對稱的情況發生,若都設計為choseDay則會造成未超過25號而月份就進入下一個月的情況。

到現在就可以做出可滑動式的日期。

接下來我想讓被點選到的日期跳至正中間的畫面

所以在func didSelectItemAt 中,利用collectionView.scrollToItem幫我們達到這個效果,透過被點選到的Cell(indexPath.row)來操作。

collectionViews.scrollToItem(at: , at: , animated: true)

第一個at:代表要顯示的Cell編號

第二個at:代表你希望它顯示在螢幕的哪裡

Animate:是否需要流暢動畫

因此先抓出每次被點選到的Cell位置,再來宣告進ScrollToItem當中

如此一來便完成嚕,做出一個可滑動可點選的datePicker

紀錄一下後續我還想加入的功能

  1. 被點選後移至中間時在數字後出現圓形圖案
  2. 滑動後能夠固定五個Cell顯示於畫面中,而不是出現6個但有2個被截一半的狀態出現
  3. 優化關於時間的設計方式

--

--