Swift

FSCalendarとRealmでさくっとカレンダー実装してみた

今回は、FSCalendarというライブラリとRealmを使用して以下のようなカレンダーを実装してみます。

この記事で説明する機能は、下記5つです。

  • カレンダーの表示
  • イベントの作成
  • 日毎のイベントの表示
  • イベントの削除
  • イベントがある日にマルポチを表示

準備

下準備として、podfileに下記3つを追加してpod installします。

pod 'FSCalendar'
pod 'CalculateCalendarLogic'
pod 'RealmSwift'

 

FSCalendar:簡単にカレンダーを表示できるライブラリです。

CalculateCalendarLogic:祝日を表示するために使用します。

RealmSwift:Realmを使ってデータを保存していきます。

カレンダーの表示

カレンダーを表示指定場所にUIViewを設置し、Classに「FSCalendar」を継承させます。

これだけでカレンダーの表示は完了です!!

選択した時の色や、今日を示す色、月や曜日の文字色は下に示したところから変更できます。

画像に示した以外にも、カスタマイズできる箇所はあるのでいろいろ試してみてください。

現状土日と祝日の色がついていません。

これに関しては、下記記事がわかりやすかったのでご参考ください。

イベントの作成

今回は、Realmを使用して端末にデータを保存していきます。

まずは、Modelを作成しましょう。

必要に応じて、プロパティの追加や型の変更をしてください。

import RealmSwift

class EventModel: Object {
    @objc dynamic var title = ""
    @objc dynamic var memo = ""
    @objc dynamic var date = "" //yyyy.MM.dd
    @objc dynamic var start_time = "" //00:00
    @objc dynamic var end_time = "" //00:00
}

 

イベントを作成する関数は、下記のようになります。

func createEvent(success: @escaping () -> Void) {
    do {
        let realm = try Realm()
        let eventModel = EventModel()
        eventModel.title = titleTextField.text ?? ""
        eventModel.memo = memoTextView.text
        eventModel.date = stringFromDate(date: date as Date, format: "yyyy.MM.dd")
        eventModel.start_time = startTextField.text ?? ""
        eventModel.end_time = endTextField.text ?? ""
        
        try realm.write {
            realm.add(eventModel)
            success()
        }
    } catch {
        print("create todo error.")
    }
}

 

日毎のイベントの表示

カレンダーで、日にちをタップしたら、TableViewにその日のイベントを表示するようにしていきます。

まず、保存されたデータの取得関数は下記です。

func getModel() {
    let results = realm.objects(EventModel.self)
    var eventModels: [[String:String]] = []
    for result in results {
        eventModels.append(["title": result.title,
                            "memo": result.memo,
                            "date": result.date,
                            "start_time": result.start_time,                            
                            "end_time": result.end_time])
    }
}

 

eventModelsに、取得したデータが[[String:String]]という辞書型で格納されるので、tableViewのCellに渡して表示させます。

この時、全てのデータのindex番号目でデータを渡してしまうと、日毎の表示にならないので下記関数で選択された日のイベントのみを取り出します。

func filterModel() {
    var filterdEvents: [[String:String]] = []
    for eventModel in eventModels {
        if eventModel["date"] == stringFromDate(date: selectedDate as Date, format: "yyyy.MM.dd") {
            filterdEvents.append(eventModel)
        }
    }
    filterdModels = filterdEvents
}

 

カレンダーの日がタップされたことを検知する、FSCalendarのDelegate関数があるのでこちらを使って表示の切り替えを行いましょう。

func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
    filterModel()
    tableView.reloadData()
}

イベントの削除

tableViewCellの削除方法は割愛し、Realmからデータを削除する方法をご紹介します。

TableViewでCellをスライド削除する方法は下記URLが参考になります。

データを削除するための関数は下記です。

全てのイベントのIndexPath番号目ではなく、選択された日が持つイベントの中でIndexPath番目を削除しないとなりません。

そこで工夫したのが、Realmからデータを取り出すときにfilterをかけたということです。

普通のfilterと書き方が違うので少々てこずりました。

下記URLの「5.filterによるクエリ」が参考になりました。
[Realm][Swift4対応 完全保存版] 4.クエリによるデータの取得
func deleteModel(selectedDate: String, indexPath: IndexPath) {
    let results = realm.objects(EventModel.self).filter("date == '\(selectedDate)'")
    do {
        try realm.write {
            realm.delete(results[indexPath.row])
            getModel()
        }
    } catch {
        print("delete data error.")
    }
}

イベントがある日にマルポチを表示

FSCalendarのDelegate関数である、「numberOfEventsFor」を使用します。

返り値はIntが指定されており、返した数だけマルポチが表示されます。

今回は、イベントがあればその数に関係なく1を返し、なければ0を返すようにしました。

この関数は日毎にFor文のような形で繰り返し呼ばれます。

したがって、eventModelのdateと引数のdateが一致していたら1を返せばいいわけです。

func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
    let date = stringFromDate(date: date, format: "yyyy.MM.dd")
    var hasEvent: Bool = false
    for eventModel in eventModels {
        if eventModel["date"] == date {
            hasEvent = true
        }
    }
    if hasEvent {
        return 1
    } else {
        return 0
    }
}
ABOUT ME
伊原 萌夏
スクール担当 兼 iOS(時々Web)エンジニア。 2019年7月に大学を一年半で辞め、大学とは別で勉強していたプログラムの世界で生きていくことを決意。翌月8月に入社。 主にSwiftのスクールを担当。

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA