Xcode
iOS
Swift
Realm

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活用編〜