post Image
Swift4 + Realmでカレンダーアプリを作ってみた。

目次

  • カレンダーアプリを作ろう!
  • 使用したもの
  • 事前準備
  • Realmを使う
  • FSCalendar&CalculateCalendarLogicを使う
  • スケジュール登録機能を追加する
  • 完成
  • 感想
  • 参考文献

カレンダーアプリを作ろう!

今回はFSCalendar + Realmを使って、スケジュール登録ができるカレンダーアプリを作ってみました。
スクリーンショット 2018-05-04 21.41.02.png

使用したもの

Xcode9.3
Swift4.1
CocoaPods
FSCalendar
CalculateCalendarLogic
Realm

※尚、今回CocoaPodsの導入は割愛します。

事前準備

まず、CocoaPodsを使って、FSCalendarとCalculateCalendarLogicとRealmをインストールする。
ターミナルで、以下のコードを記入し実行。

pod init

これで、Podfileが生成される。
次に、Podfileを以下の様に編集する。

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'xxx' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # 以下の行を追加
  pod 'RealmSwift'
  pod 'FSCalendar'
  pod 'CalculateCalendarLogic'
  # 追加ここまで
 
  # Pods for xxx

  target 'xxxTests' do
  # 以下略

そして、ターミナルで、

pod install

を実行する。
これで、インストール完了。

Realmを使う

まず、RealmでDB設定を行う。

今回は、

  • 日付(date)
  • スケジュール(event)

を登録する。

Command + Nキーを押す->iOS->SourceからSwift Fileを選択し、Swiftファイルを新規作成。

スクリーンショット 2018-05-04 22.08.26.png

作成したSwiftファイルに、以下のコードを記述する。

Event.swift
import Foundation
import RealmSwift

class Event: Object {

    @objc dynamic var date: String = ""
    @objc dynamic var event: String = ""

}

これで、RealmのDB設定は完了。

FSCalendar&CalculateCalendarLogicを使う

次に、FSCalendar&CalculateCalendarLogicを使って、カレンダー機能を実装する。
簡単に説明すると、

  • FSCalendar -> カレンダー機能を実装する
  • CalculateCalendarLogic -> 土日・祝日の判定機能を実装する

という感じだ。FSCalendarには、祝日を判別する機能はないので、そこをCalculateCalendarLogicで補っている。
ViewController.swiftに以下のコードを記入。

ViewController.swift
import UIKit
import FSCalendar
import CalculateCalendarLogic
import RealmSwift

//ディスプレイサイズ取得
let w = UIScreen.main.bounds.size.width
let h = UIScreen.main.bounds.size.height

class ViewController: UIViewController, FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance {
    //スケジュール内容
    let labelDate = UILabel(frame: CGRect(x: 5, y: 580, width: 400, height: 50))
    //「主なスケジュール」の表示
    let labelTitle = UILabel(frame: CGRect(x: 0, y: 530, width: 180, height: 50))
    //カレンダー部分
    let dateView = FSCalendar(frame: CGRect(x: 0, y: 30, width: w, height: 400))
    //日付の表示
    let Date = UILabel(frame: CGRect(x: 5, y: 430, width: 200, height: 100))
    override func viewDidLoad() {
        super.viewDidLoad()
        //カレンダー設定
        self.dateView.dataSource = self
        self.dateView.delegate = self
        self.dateView.today = nil
        self.dateView.tintColor = .red
        self.view.backgroundColor = .white
        dateView.backgroundColor = .white
        view.addSubview(dateView)

        //日付表示設定
        Date.text = ""
        Date.font = UIFont.systemFont(ofSize: 60.0)
        Date.textColor = .black
        view.addSubview(Date)

        //「主なスケジュール」表示設定
        labelTitle.text = ""
        labelTitle.textAlignment = .center
        labelTitle.font = UIFont.systemFont(ofSize: 20.0)
        view.addSubview(labelTitle)

        //スケジュール内容表示設定
        labelDate.text = ""
        labelDate.font = UIFont.systemFont(ofSize: 18.0)
        view.addSubview(labelDate)

        //スケジュール追加ボタン
        let addBtn = UIButton(frame: CGRect(x: w - 70, y: h - 70, width: 60, height: 60))
        addBtn.setTitle("+", for: UIControlState())
        addBtn.setTitleColor(.white, for: UIControlState())
        addBtn.backgroundColor = .orange
        addBtn.layer.cornerRadius = 30.0
        addBtn.addTarget(self, action: #selector(onClick(_:)), for: .touchUpInside)
        view.addSubview(addBtn)


    }

    fileprivate let gregorian: Calendar = Calendar(identifier: .gregorian)
    fileprivate lazy var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()

    // 祝日判定を行い結果を返すメソッド
    func judgeHoliday(_ date : Date) -> Bool {
        //祝日判定用のカレンダークラスのインスタンス
        let tmpCalendar = Calendar(identifier: .gregorian)

        // 祝日判定を行う日にちの年、月、日を取得
        let year = tmpCalendar.component(.year, from: date)
        let month = tmpCalendar.component(.month, from: date)
        let day = tmpCalendar.component(.day, from: date)

        let holiday = CalculateCalendarLogic()

        return holiday.judgeJapaneseHoliday(year: year, month: month, day: day)
    }

    // date型 -> 年月日をIntで取得
    func getDay(_ date:Date) -> (Int,Int,Int){
        let tmpCalendar = Calendar(identifier: .gregorian)
        let year = tmpCalendar.component(.year, from: date)
        let month = tmpCalendar.component(.month, from: date)
        let day = tmpCalendar.component(.day, from: date)
        return (year,month,day)
    }

    //曜日判定
    func getWeekIdx(_ date: Date) -> Int{
        let tmpCalendar = Calendar(identifier: .gregorian)
        return tmpCalendar.component(.weekday, from: date)
    }

    // 土日や祝日の日の文字色を変える
    func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? {
        //祝日判定をする
        if self.judgeHoliday(date){
            return UIColor.red
        }

        //土日の判定
        let weekday = self.getWeekIdx(date)
        if weekday == 1 {
            return UIColor.red
        }
        else if weekday == 7 {
            return UIColor.blue
        }

        return nil
    }

}

スケジュール登録機能を追加する

まず、スケジュール登録ページ(EventViewController.swift)を追加する。
Command + Nキーを押す->iOS->SourceからCocoa Touch Classを選択し、新規作成。

スクリーンショット 2018-05-04 22.08.11.png

そして、Main.StoryBoardView Controllerを追加し、
Custom ClassclassEventViewControllerにする。

そして、各StoryBoard IDを以下の様にする。

ViewController -> Main
EventViewController -> Insert

まずは、画面遷移を実装する。
ViewController.swiftに以下のコードをclass内に追加する。

ViewController.swift
    //画面遷移(スケジュール登録ページ)
    @objc func onClick(_: UIButton) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let SecondController = storyboard.instantiateViewController(withIdentifier: "Insert")
        present(SecondController, animated: true, completion: nil)
    }

次に、スケジュール登録ページを実装する。
以下のコードをEventViewController.swiftに記入。

EventViewController.swift
import UIKit
import RealmSwift
//ディスプレイサイズ取得
let w2 = UIScreen.main.bounds.size.width
let h2 = UIScreen.main.bounds.size.height
//スケジュール内容入力テキスト
let eventText = UITextView(frame: CGRect(x: (w2 - 300) / 2, y: 100, width: 300, height: 200))

//日付フォーム(UIDatePickerを使用)
let y = UIDatePicker(frame: CGRect(x: 0, y: 300, width: w2, height: 300))
//日付表示
let y_text = UILabel(frame: CGRect(x: (w2 - 300) / 2, y: 570, width: 300, height: 20))
class EventViewController: UIViewController {
    var date: String!
    override func viewDidLoad() {
        super.viewDidLoad()

        //スケジュール内容入力テキスト設定
        eventText.text = ""
        eventText.layer.borderColor = UIColor.gray.cgColor
        eventText.layer.borderWidth = 1.0
        eventText.layer.cornerRadius = 10.0
        view.addSubview(eventText)

        //日付フォーム設定
        y.datePickerMode = UIDatePickerMode.date
        y.timeZone = NSTimeZone.local
        y.addTarget(self, action: #selector(picker(_:)), for: .valueChanged)
        view.addSubview(y)

        //日付表示設定
        y_text.backgroundColor = .white
        y_text.textAlignment = .center
        view.addSubview(y_text)

        //「書く!」ボタン
        let eventInsert = UIButton(frame: CGRect(x: (w2 - 200) / 2, y: 600, width: 200, height: 50))
        eventInsert.setTitle("書く!", for: UIControlState())
        eventInsert.setTitleColor(.white, for: UIControlState())
        eventInsert.backgroundColor = .orange
        eventInsert.addTarget(self, action: #selector(saveEvent(_:)), for: .touchUpInside)
        view.addSubview(eventInsert)

        //「戻る!」ボタン
        let backBtn = UIButton(frame: CGRect(x: (w - 200) / 2, y: h - 50, width: 200, height: 30))
        backBtn.setTitle("戻る", for: UIControlState())
        backBtn.setTitleColor(.orange, for: UIControlState())
        backBtn.backgroundColor = .white
        backBtn.layer.cornerRadius = 10.0
        backBtn.layer.borderColor = UIColor.orange.cgColor
        backBtn.layer.borderWidth = 1.0
        backBtn.addTarget(self, action: #selector(onbackClick(_:)), for: .touchUpInside)
        view.addSubview(backBtn)

    }

    //画面遷移(カレンダーページ)
    @objc func onbackClick(_: UIButton) {
        dismiss(animated: true, completion: nil)
    }

    //日付フォーム
    @objc func picker(_ sender:UIDatePicker){
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd"
        y_text.text = formatter.string(from: sender.date)
        view.addSubview(y_text)
    }

    //DB書き込み処理
    @objc func saveEvent(_ : UIButton){
        print("データ書き込み開始")

            let realm = try! Realm()

            try! realm.write {
                //日付表示の内容とスケジュール入力の内容が書き込まれる。
                let Events = [Event(value: ["date": y_text.text, "event": eventText.text])]
                realm.add(Events)
                print("データ書き込み中")
            }

        print("データ書き込み完了")

        //前のページに戻る
        dismiss(animated: true, completion: nil)

    }

}

DBに書き込んだ値を取得し、カレンダーに反映させる。
Viewcontroller.swiftに以下のコードを追加する。

ViewController.swift
//カレンダー処理(スケジュール表示処理)
    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition){

        labelTitle.text = "主なスケジュール"
        labelTitle.backgroundColor = .orange
        view.addSubview(labelTitle)

        //予定がある場合、スケジュールをDBから取得・表示する。
        //無い場合、「スケジュールはありません」と表示。
        labelDate.text = "スケジュールはありません"
        labelDate.textColor = .lightGray
        view.addSubview(labelDate)

        let tmpDate = Calendar(identifier: .gregorian)
        let year = tmpDate.component(.year, from: date)
        let month = tmpDate.component(.month, from: date)
        let day = tmpDate.component(.day, from: date)
        let m = String(format: "%02d", month)
        let d = String(format: "%02d", day)

        let da = "\(year)/\(m)/\(d)"

        //クリックしたら、日付が表示される。
        Date.text = "\(m)/\(d)"
        view.addSubview(Date)

        //スケジュール取得
        let realm = try! Realm()
        var result = realm.objects(Event.self)
        result = result.filter("date = '\(da)'")
        print(result)
        for ev in result {
            if ev.date == da {
                labelDate.text = ev.event
                labelDate.textColor = .black
                view.addSubview(labelDate)
            }
        }



    }

問題なければ、ここで完成。

完成

・ 起動時
スクリーンショット 2018-05-04 21.40.29.png

・ スケジュール登録ページ
スクリーンショット 2018-05-04 21.40.44.png

・ 日付選択(スケジュールなし)
スクリーンショット 2018-05-04 21.41.17.png

・ 日付選択(スケジュールあり)
スクリーンショット 2018-05-04 21.41.02.png

感想

思ってたより、大変でした。
スケジュール登録機能が凄く大変でした。
Realmが全然ダメダメだったので、勉強する必要がありますね。。。。

参考文献

Swiftで簡単にカレンダーを作ろう!(FSCalendar)
Swiftで日記アプリを作ろう 〜その3 Realm活用編〜


『 Swift 』Article List