iOS の TodayExtension を実装していて、 TodayViewController の viewDidLoad が走るタイミングが最初はわからず苦労した。
viewDidLoad が呼ばれるタイミング
画面に表示されたタイミングで ほぼ毎回 viewDidLoad が呼ばれる。
TodayExtension を 10 こくらい並べて、スクロールして画面外から表示させても viewDidLoad が呼ばれる。
開発し始めた最初は
- 端末起動後/Widget 追加後に viewDidLoad が 1 度だけ呼ばれる
- 画面表示時に widgetPerformUpdate(completionHandler:) が呼ばる
- 更新の必要があれば
completionHandler(.newData)
を実行する
- 更新の必要があれば
だと思って開発を始めたため、何度も viewDidLoad が呼ばれて戸惑った。
Today Extension 作ってて、 viewDidLoad がめっちゃ呼ばれて開くたびに高さが変わっちゃうんだけどこれなんなの、本番じゃないから?
— スターホシ (@star__hoshi) 2017年5月16日
みんなこれで困ってないの...
TodayExtension の高さを変えるような時に問題が生じる
TodayExtension Widget 表示時、こう動く。(TableView で、 List の要素 1 つにつき 50px 使うようなコードだとする。)
- 一瞬だけ前回取得したデータが表示される
- 要素が 4 つあったので、 height が 200px くらいある
- Extension の表示が初期表示になる
- 初期は要素数が 0 なので、 height がデフォルト(110px)になる
- 取得されたデータが表示される
- しかし、 Show More になっているのにデータが2つしか表示されない…
Show More の状態は変わらないが画面はリセットされてしまい、表示がおかしい…。
こんな感じ。
表示を改善するためには?
初期表示時に前回取得したデータを表示すれば良い。
- 一瞬だけ前回取得したデータが表示される
- 前回の height が 200px だったので、それがそのまま表示される
- Extension を初期化する
- 初期化時にローカルに保存しておいたデータを表示する
- 画面に変化がないので height 200px を保つ
- 初期化時にローカルに保存しておいたデータを表示する
- 取得された最新のデータを表示する
- 要素数が 4 つだったので、 height が 200px になる
- 取得したデータをローカルに保存しておく
つまり、前回取得したデータを保存しておいて、 viewDidLoad 時にそのデータを描画するとユーザとしては画面がチラチラせずに違和感なく使える。
そうすると初期化時に高さが前回と同じに設定されるので、突然小さくなったりしないですむ。
↓のは画面に変化がないように見えるが、実際には画面が初期化されたりしている。
ローカルにデータを保存
保存したいデータが String だけとかなら楽でいいけど、実際は JSON Mapping されたクラスだったりするので、そのクラスに NSCoding を継承して data 型として保存すると楽。
UserDefault などに class を保存するサンプル
ちなみに TodayExtension で UserDefaults を使うには AppGroups の設定が必要。
おわり
最初は viewDidLoad が呼ばれまくることに違和感があるが、 UserDefaults などを使って適切にデータを管理すればそんなに難しくなかった。
しかし Show More / Show Less の挙動とかうまくいかないところとかあってなかなか難しい…。