LoginSignup
0
0

FSCalendarでGoogleCalendar風のUIを作成する

Posted at

はじめに

この記事はSwiftのカレンダーライブラリであるFSCalendarを使ってGoogleCalendarのようなUIを実現する方法について解説しています。

環境

  • Xcode15.0.1
  • Swift5.9

完成図

Simulator Screenshot - iPhone 15 Plus - 2023-12-20 at 20.46.55.png

Google Calendarを意識しました。

Screenshot 2023-12-20 at 15.07.43.png

開発の手順

  1. FSCalendarの準備
  2. カスタムのカレンダーセルを作成
  3. TableViewセルを作成
  4. 予定の反映

1. FSCalendarの準備

FSCalendarのインポート、およびStoryboardの準備をします。(詳細は省略します。)
Screenshot 2023-12-20 at 21.23.05.png

2. カスタムのカレンダーセルを作成

デフォルトで登録されているセルを使用せず、自分でカレンダーのセルを作成します。
FSCalendarCellというクラスがあるはずです。今回はStoryboardで作成したいので、Also create XIB fileも忘れずにチェックします!
Screenshot 2023-12-20 at 21.32.18.png

作成されたセルにLabelとTableViewを配置しました。今回は複数の予定をTableViewで表示します。

Screenshot 2023-12-20 at 21.36.36.png

セルのサイズは50*75くらいがうまくおさまるというところでしょうか。後にオートレイアウトで調整します。

3. TableViewセルを作成

今回はGoogle Calendar風のUIにしたので、このように作りました。先ほどCalendar Cellの高さを75に設定したので、TableView Cellの高さは20にしておきました。2つほど予定が表示できる計算です。
Screenshot 2023-12-20 at 21.39.30.png

コードはパーツを宣言して関連付けしておけばOKです。

CalendarEventTableViewCell
import UIKit

class CalendarEventTableViewCell: UITableViewCell {
    @IBOutlet var eventLabel: UILabel!
    @IBOutlet var eventImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
}

4. Calendar Cellの内容

Calendar Cellは難しくありません!CollectionViewCellやTableViewCellの内容を書くのと同じ容量で書けます。

import UIKit
import FSCalendar

class CustomCalendarCell: FSCalendarCell, UITableViewDataSource, UITableViewDelegate {
    
    @IBOutlet var dateLabel: UILabel!
    @IBOutlet var eventTableView: UITableView!
    var eventList: [TaskObject]! //日付ごとの予定を格納する配列を作成
    
    override func awakeFromNib() {
        super.awakeFromNib()
        self.titleLabel.isHidden = true //Calendar Cellのデフォルトの日付を消す
        self.loadTableView()
        
    }

    //TableViewの初期設定を関数にまとめました。
    func loadTableView() {
        self.eventTableView.delegate = self
        self.eventTableView.dataSource = self
        self.eventTableView.register(UINib(nibName: "CalendarEventTableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
        self.eventTableView.rowHeight = 20
        self.eventTableView.allowsSelection = false //TableViewセルを選択不可にする
        self.eventTableView.separatorStyle = .none
        self.eventTableView.reloadData()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.eventList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CalendarEventTableViewCell
        cell.eventLabel.text = eventList[indexPath.row].taskname
        return cell
    }
    
}

ちょっとしたポイントとして、self.eventTableView.allowsSelection = false でTableViewCellを選択不可にすることがあります。
セルを選択できてしまうと、日付を選択しようとした時にTableViewのセルが反応してしまい鬱陶しいので...

TableViewに表示する内容はvar eventList: [TaskObject]!という配列でFSCalendarを置いているViewControlllerから受け取ります。今回は省略しますが、日々の予定をTaskObjectというStructで作りました。Structについて詳しくはこちらから。

struct TaskObject{
    let taskname:String
    let deadline:Date
    let uid:String
    let finished:Bool
}

5. CalendarViewControllerのコードを書く

基本的なFSCalendarの設定は終わっている前提で、カスタムの CalendarCellを登録するにあたって特筆すべき点だけ書きます!

CalendarViewController
override func viewDidLoad() {
        super.viewDidLoad()
        
        setCalendar()
    }
    func setCalendar(){
        self.calendar.dataSource = self
        self.calendar.delegate = self
        self.calendar.collectionView.register(UINib(nibName: "CustomCalendarCell", bundle: nil),forCellWithReuseIdentifier: "cell") //Calendar カスタムセルを登録する
        calendar.rowHeight = 75 
        calendar.headerHeight = 20
        calendar.appearance.selectionColor = .clear //選択した日付の色が変わるのを防ぐ(透明にする)
        calendar.reloadData()
    }

基本的にカスタムセルを登録する方法はTableViewやCollectionViewと同じです!
FSCalendarの日付選択を無効化することはできなさそうなので、calendar.appearance.selectionColor = .clearで選択した際の色を透明にして見かけ上選択していないようにしています。

また、同様にセルの選択も瞬時に解除されるように以下コードを加えました。

CalendarViewController
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
        calendar.deselect(date)
    }

最後にCalendarCellの中身を書きます。

CalendarViewController
func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell {
        let cell = calendar.dequeueReusableCell(withIdentifier: "cell", for: date, at: position) as! CustomCalendarCell
        cell.dateLabel.text = String(getDay(date).2)
        let tasksForDate = task.filter { task in
                Calendar.current.isDate(task.deadline, inSameDayAs: date)
            }
            
        cell.eventList = tasksForDate
        
        return cell
    }

各セルの日付と、その日付に紐づく予定をCalendarCellに渡しています。TableViewCellやCollectionViewCellと同じですね!

cell.dateLabel.text = String(getDay(date).2)

で、各セルの日付を取得し自分で作った日付のラベルに表示しています。
参考までに、getDayはこのように実装しています。

CalendarViewController
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)
    }

次に、読み込んだ予定を日付でフィルタリングします。

CalendarViewController
let tasksForDate = task.filter { task in
                Calendar.current.isDate(task.deadline, inSameDayAs: date)
    }
            
cell.eventList = tasksForDate

絞り込んだ予定をCalendarCellに送り、CalendarCellからさらにTableViewCellに送られて表示されるというわけです!

まとめ

意外と簡単でしたね!CustomCellの使い方に慣れていればできそうです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0