close

[Swift5]FSCalendarがトップ画面で再描画されない[試行]

公開日:

概容

トップページでイベントがある日付にドットを表示しています。

新規作成画面に遷移し、例えば、11月7日に予定を新規作成したとします。

しかし、dismissでトップ画面に戻ったとしてもドットは7日に追加されません。

試行1

ドットを付ける関数の発動のタイミングをViewWillAppear内にする(失敗)

ドットを表示する関数

func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date)    -> Int {     
   if self.RemovedTodayDateFormattedDateArray.contains(date){            //予定が1つでも含まれていれば1を返す         
   return 1     
   }     
return 0 
}

CalendarDelegate.swiftを作成

import Foundation
import FSCalendar

protocol CalendarDelegate {
    //カレンダーに予定がある日付にドットを付ける関数を規定
    func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int
}

ViewController内で以下を記述

class ViewController: UIViewController,FSCalendarDataSource, FSCalendarDelegate,FSCalendarDelegateAppearance,CalendarDelegate {
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
//デリゲートをインスタンス化
    var calendarDelegate: CalendarDelegate?
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
//ViewWillAppear内にて。DateArrayは予定がある日付を格納している。
for i in DateArray{
    calendarDelegate?.calendar(self.calendar, numberOfEventsFor: i)
    }

分かったこと

・func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {

のdateはカレンダーの月単位ページ描画時に1ヶ月分の日付が格納されて、日付の数だけこの関数が呼ばれる。

ですので、アプリ初回起動時 + カレンダーを横スクロールすると再取得されて、ドットが表示されます。

・上の関数の返り値Intは@protocol FSCalendarDataSource <NSObject>のオブジェクト(?)の

中のオプショナル変数(?)のnumberOfEventsForDateに格納され、ライブラリ内のどこかで返り値を管理している。

結論

よってFSCalendarのprotocolから移譲されているfuncを、自作protocolで再定義してViewWillAppear内で呼んでも、その入れ子状態になっているfunc(の返り値)をライブラリ側で認識し得ないため、「Result of call to ‘XXXXX’ is unused」エラーとなる。よってViewWillAppearで実行するなど、実行タイミングは絶対に変えられないと分かった。

試行2

検討

・トップページのUIView(calendar)がNewTaskViewControllerにshow/present Mordaryして トップページに戻ってきてもCalendarが描画し続けている点がおかしい。(一度更新さえされたら問題は解決します。)

比較として別のViewControllerに配置したFSCalendarは開くたびに再描画されているため、スクロール位置も元に戻っています。

・UIViewのライフサイクルを調べ、ボタンで再描画が可能にならないか

テスト的に ViewController内のボタンクリック関数内で

//        self.calendar.setNeedsDisplay()

//        self.calendar.layoutIfNeeded()

//        self.calendar.setNeedsLayout()

//        self.calendar.updateConstraints()

//        self.calendar.updateConstraintsIfNeeded()

を記述することで再描画するか試したが状況は全く同じ(描画し続ける)

・UIViewをFSCalendarとした時点でUIViewのライフサイクルは一切無関係になっている(?)→UIView内の描画のタイミングは専用関数を読み込んでいるため?

ViewWillAppear/ViewDidAppear内に以下を移動。

calendar.delegate = self
calendar.dataSource = self 

結果:ViewDidAppearに書いた場合は以下を読み込まなくなった

func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {

試行2の結論

ライブラリを読み込んだUIViewの再描画はViewControllerではできない。

試行3

再描画が必要なポイントは予定が追加、削除された場合にドット表記が更新されないという点のみ

よって画面をdissmissで戻った際に選択日時(selectDate)を変更し、
別の月に横スクロールさせる。と同時に、カレンダーを非表示にする。

・この選択日時は今日の日付の月からマイナス1すれば解決しそうだ。

カレンダー表示ボタンをクリックしたときに選択日時を今日に変更し、
カレンダーの月を今月に横スクロールさせる。

・表示非表示は高さの変更でうまくいきそうだ。

問題発生

FSCalendarはFSCalendarクラスを引き継いでおり、UIViewクラスは継承していないことに気づく

よってUIView.animation などは使用できない。

試行3-1

@IBAction func calendarButton(_ sender: Any) {   
   if self.calendar.fs_height >= 1{         
      self.calendar.fs_height = 0     
      }else{         
      self.calendar.fs_height = 300     
   }
}

上のコードでいわゆる「トルツメ」を行ってみた。

結果3-1

中身が描画されず。

おそらく高さによって自動的に描画を変更する関数がFSCalendarには組み込まれており上記の方法では認識しない。

まとめ

プロトコルから以上されている返り値がある関数の発動タイミングを変える方法はありません。

この後、筆者は別の方法で解決することになるのだが、

それはまた別の話。

Comment

メールアドレスが公開されることはありません。