TodayViewController の viewDidLoad が呼ばれるタイミングについて

iOS の TodayExtension を実装していて、 TodayViewController の viewDidLoad が走るタイミングが最初はわからず苦労した。

viewDidLoad が呼ばれるタイミング

画面に表示されたタイミングで ほぼ毎回 viewDidLoad が呼ばれる。
TodayExtension を 10 こくらい並べて、スクロールして画面外から表示させても viewDidLoad が呼ばれる。

開発し始めた最初は

  1. 端末起動後/Widget 追加後に viewDidLoad が 1 度だけ呼ばれる
  2. 画面表示時に widgetPerformUpdate(completionHandler:) が呼ばる
    • 更新の必要があれば completionHandler(.newData) を実行する

だと思って開発を始めたため、何度も viewDidLoad が呼ばれて戸惑った。

TodayExtension の高さを変えるような時に問題が生じる

TodayExtension Widget 表示時、こう動く。(TableView で、 List の要素 1 つにつき 50px 使うようなコードだとする。)

  1. 一瞬だけ前回取得したデータが表示される
    • 要素が 4 つあったので、 height が 200px くらいある
  2. Extension の表示が初期表示になる
    • 初期は要素数が 0 なので、 height がデフォルト(110px)になる
  3. 取得されたデータが表示される
    • しかし、 Show More になっているのにデータが2つしか表示されない…

Show More の状態は変わらないが画面はリセットされてしまい、表示がおかしい…。
こんな感じ。

f:id:star__hoshi:20170720140917g:plain

表示を改善するためには?

初期表示時に前回取得したデータを表示すれば良い。

  1. 一瞬だけ前回取得したデータが表示される
    • 前回の height が 200px だったので、それがそのまま表示される
  2. Extension を初期化する
    • 初期化時にローカルに保存しておいたデータを表示する
      • 画面に変化がないので height 200px を保つ
  3. 取得された最新のデータを表示する
    • 素数が 4 つだったので、 height が 200px になる
    • 取得したデータをローカルに保存しておく

つまり、前回取得したデータを保存しておいて、 viewDidLoad 時にそのデータを描画するとユーザとしては画面がチラチラせずに違和感なく使える。

そうすると初期化時に高さが前回と同じに設定されるので、突然小さくなったりしないですむ。

↓のは画面に変化がないように見えるが、実際には画面が初期化されたりしている。

f:id:star__hoshi:20170720140853g:plain

ローカルにデータを保存

保存したいデータが String だけとかなら楽でいいけど、実際は JSON Mapping されたクラスだったりするので、そのクラスに NSCoding を継承して data 型として保存すると楽。

UserDefault などに class を保存するサンプル

ちなみに TodayExtension で UserDefaults を使うには AppGroups の設定が必要。

おわり

最初は viewDidLoad が呼ばれまくることに違和感があるが、 UserDefaults などを使って適切にデータを管理すればそんなに難しくなかった。

しかし Show More / Show Less の挙動とかうまくいかないところとかあってなかなか難しい…。