髪も切れるiOSエンジニアのブログ

元美容師エンジニアの成長と奮闘の記録

自作家計簿アプリ製作の軌跡②-2 〜Calendarの実装〜

前回からの続きです。
自作家計簿アプリ製作の軌跡②-1 〜Calendarの実装〜 - 髪も切れるiOSエンジニアのブログ
今回はその月のカレンダーに表示する日付を取得できるようにしていきます。

環境

xcode 8
swift 3.0

作業切り分け

1、CalendarCellクラスを追加 ⇨ 完了
2、一旦CollectionViewを最低限実装する ⇨ 完了
3、DateManagerクラスの追加 ⇨ ここから
3-1、その月のカレンダーの最初の週の日曜日が何日かを取得する
3-2、その月のカレンダーの最後の週の土曜日が何日かを取得する
3-3、-1から-2までが何日あるかを取得する

実装

3、DateManagerクラスの追加

まずはカレンダーの日付取得専用のクラス、DateManager.swiftクラスファイルを追加しましょう。
後に必要になる変数定数の宣言もしておきます。

import Foundation

class DateManager: NSObject {

    var selectedDay = Date()
    var beginningDate = Date()
    var lastDate = Date()
    let calendar = Calendar.current

}

このクラス内で、
・その月のカレンダーの最初の週の日曜日が何日か
・その月のカレンダーの最後の週の土曜日が何日か
・その月のカレンダーの表示に必要なセルの数
を一括で管理できるようにします。

3-1、その月のカレンダーの最初の週の日曜日が何日かを取得する

    /// カレンダーの起点の日付を取得
    func firstDateOfMonth() -> Date {
        /// 現在日付のコンポーネント
        var components = calendar.dateComponents([.year,.month,.day], from: selectedDay)
        // 日付の要素を1日に指定
        components.day = 1
        /// 今月1日のデータ
        let firstDate = Calendar.current.date(from: components)
        /// 今月1日の曜日
        let weekOfFirstDay = calendar.component(.weekday, from:firstDate!)
        /// 取得した1日の曜日から、同じ週の日曜日が何日になるかを取得する
        let startDateOfCalender = calendar.date(byAdding: .day, value: 1 - weekOfFirstDay, to: firstDate!)!
        return startDateOfCalender
    }

ここでポイントとなるのは、「今月1日が何曜日なのか?」です。
let weekOfFirstDay = calendar.component(.weekday,from:firstDate!)
で曜日を取得すると、曜日のindex番号が返却されます。
日曜日 ⇨ 1
月曜日 ⇨ 2
・・・
土曜日 ⇨ 7
(取得した曜日のindex番号 - 1)日分日付を戻せば前の月の最後の日曜日の日付(=今月のカレンダーの最初の日曜日の日付)を求めることができます。
let startDateOfCalender = calendar.date(byAdding: .day, value: 1 - weekOfFirstDay, to: firstDate!)!

3-2、その月のカレンダーの最後の週の土曜日が何日かを取得する

    func endDateOfMonth() -> Date {
        /// 今月の最後の日のデータ
        let lastDate = calendar.nextDate(after: selectedDay, matching: DateComponents(day:1), matchingPolicy: Calendar.MatchingPolicy.nextTime)
        /// 今月の最後の日付の曜日
        let weekOfLastDate = calendar.component(.weekday,from: lastDate!)
        // 今月の最後の日付の曜日を調べて、その要素数だけ進んだものが右下(次の月の初めで計算している事に注意)
        let lastDateOfCalender = calendar.date(byAdding: .day, value: 7 - weekOfLastDate, to: lastDate!)!
        return lastDateOfCalender
   }

3-1と同様に今度は最終週の土曜日の日付を取得しています。

3-3、3-1から3-2までが何日あるかを取得する

まずは現在の月のカレンダー表示に必要なセルの個数を決定します。

    /// 現在の月のセルの数を返すメソッド
    func dateCountInCurrentMonth() -> Int {
        // 始まりの日と終わりの日を取得
        beginningDate = firstDateOfMonth()
        lastDate = endDateOfMonth()
        // 始点から終点の日数
        return calendar.dateComponents([.day], from:beginningDate ,to:lastDate).day! + 1
    }

return calendar.dateComponents([.day], from:beginningDate ,to:lastDate).day! + 1
最後に"+ 1"していることに注意です。
カレンダーの始点から終点までの日数をそのまま返却してしまうとセルの数としては一つ足りません。

次にコレクションViewのindex番号から各日付のセルに表示していく日にちの文字列を返すメソッドを追加します

    /// カレンダーの始点から引数で渡ってきたカレンダーセルのインデックスを加算した日付の文字列を返す
    func convertDateFormat(index: Int) -> String {
        /// カレンダーの起点の日付にカレンダーセルのインデックス番号を足した日にち
        let date = calendar.date(byAdding: .day, value: index, to: beginningDate)
        return calendar.component(.day, from: date!).description
    }

これでDateManagerクラスの実装は完了。

カレンダークラスの修正

あとはカレンダーを表示しているクラスからDateManagerクラスを呼び、必要なメソッドを利用していきます。

//// CalendarViewController.swift
    /// カレンダーのセルの個数を返す
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // return 30
        return dateManager.dateCountInCurrentMonth()
    }
//// CalendarViewController.swift
    /// 各セルに日付を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CalendarCell", for: indexPath) as! CalendarCell
        //// ここ追加
        cell.dateNumber.text = dateManager.convertDateFormat(index: indexPath.row)
        return cell
    }

セルのindex番号を引数にconvertDateFormatメソッドを呼ぶだけです。
実行してみましょう。
f:id:Tansok:20161119134204p:plain

完成!

最後に

この記事を書いてて気付きましたが、
現在の日付データが一貫してグリニッジ標準時で作ってます。
日本の標準時にしないと月末月初で影響でちゃう?なんて懸念点があります。
対応しないとね。