fkm blog

software開発に関することを書いていきます

UIScrollViewにauto layout付きカスタムViewを貼ってはまった話

iOSのUIScrollViewはページ単位でスクロールできるので, AndroidのViewPagerのように, 動的にViewを貼り付けて使うこともあると思う.

で, 動的に追加するViewをxibファイルでカスタムView化するケースもあると思うのですが, この組み合わせでかなりはまったのでメモ.

前提をまとめておくと

  • auto layoutあり
  • ページの中身になるカスタムView(xibから作る)はauto layoutあり

ユーザーの操作はこんな感じ.

  1. アプリを起動する. ページは3つ
  2. viewDidLayoutSubviewsでUIScrollViewのcontentSizeを設定
  3. 別の画面で操作し, 戻ってくる
  4. 戻ってきたとき, ページを追加
  5. viewDidLayoutSubviewsでUIScrollViewのcontentSizeを設定

iOS 7であれば, 何故か別に問題は起きないが, iOS 6では最後のviewDidLayoutSubviewsで

Auto Layout still required after executing -layoutSubviews

と言われて落ちる. UIScrollViewのcontentSizeはauto layoutありなので, viewDidLayoutSubviewsで設定してあげないと反映されない. 逆にこれを抜いて, ページを追加する部分でcontentSizeの調整を行うと, 初回はうまくいくが, ページの追加削除がはいった時に反映されず, 初回の大きさになる.

いろいろやってみた結果, viewDidLayoutSubviewsでdispatch_async()で更新を試みるとうまくいく. viewWillLayoutSubviewsなど, 他のタイミングで実行すると, viewDidLayoutSubviewsの前に実行されてしまい, viewDidLayoutSubviewsの時点で昔の値のままになってしまうので注意.

- (void)viewDidLayoutSubviews {
    dispatch_async(dispatch_get_main_queue(), ^{
        UIView *shopRoot = self.scroll.subviews[0];
        self.scroll.contentSize = shopRoot.bounds.size;
    });
}