Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

目次

  • カレンダーアプリを作ろう!
  • 使用したもの
  • 事前準備
  • 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活用編〜

halprogramming
専門学校HALのプログラミングが好きな人が集まっている同好会です。HALの在学生に限らず、OB, OGなども存在します。
https://www.hal.ac.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした