iOS11 + Xcode9.0でedgesForExtendedLayoutの値を空にしていると、UITableViewのドリルダウンでアニメーションが崩れる


背景

iOS7からUIViewControllerへ追加されたプロパティに、edgesForExtendedLayoutがあります。

このプロパティはNavigation Barの下にViewへ回り込ませるためのもので、デフォルトでは回り込むように値が設定されています。

ここへ空を代入するか、Storyboardで「Under Top Bars」のチェック外すと、回り込みがなくなり、ViewはNavigation Barの下からスタートします。

edgesForExtendedLayout = []

ちょっとわかりにくいですが、Navigation Barの下にはTableViewが回り込んでいません。

発生した問題

この状態のアプリをXcode9.0でビルドすると問題が発生します。iOS11端末へ入れたときにTableViewだと画面遷移のアニメーションが不自然になります。

微妙ですが、ナビゲーションを上る時に20ptほどの隙間が存在していて、上にスクロールしながら遷移します。edgesForExtendedLayoutをいじっていない時は以下のような挙動になります。こちらはきれいに遷移しています。

原因と解決策

調べてみると、こちらにバグレポートとして似たような現象が報告されていました。

iOS11からUIScrollViewへ導入されたadjustedContentInsetが悪さをしているようです。

このプロパティは、UINavigationControllerへ配置されたUIScrollViewに対して自動でInset値を付けてくれるものです。
UIScrollViewへ自前でcontentInsetsを設定していると、このプロパティの影響でレイアウト崩れを起こすという報告がいくつかされています。

対策としては、contentInsetAdjustmentBehaviorの値にneverを設定すればいいようです。

if #available(iOS 11.0, *) {
    tableView.contentInsetAdjustmentBehavior = .never
}

この設定をしてみた所、今回のケースでも正常な状態となり、自然なアニメーションへ戻りました。

他のケースと異なり、UITableViewで特にcontentInsetsを設定していなくてもadjustedContentInsetの影響を受けることがあったので、こちらへまとめておきます。
バグレポートとして報告されていることから、将来のバージョンでは修正されているかもしれません。

追記 (2017/10/11) : contentInsetAdjustmentBehaviorをneverにした時のiPhoneXでのSafeArea問題

上記設定をした時に気をつける必要がある問題として、UIScrollViewが勝手にやってくれるSafe Area設定が無効になってしまうという点があります。例えば、下部にUITabBarなどを置かずにUITableViewが一番下に接触している場合、iPhoneXで以下の状態になります。 Home Indicatorと一番下のセルが被ってしまい、ガイドライン的によろしくない状態となってしまいます。

その問題を解消するために、frameやinsetsの値を変更する時のタイミングなどで、safeAreaInsetsのbottomを入れてやる必要があるでしょう。(safeAreaInsetsの全てをそのまま設定するとLandscapeにした時にTableViewでは左右のレイアウトが崩れてしまいます。)

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    if #available(iOS 11.0, *) {
        tableView.contentInset.bottom = view.safeAreaInsets.bottom
    }
}

そうすると以下のようにHome Indicatorの領域の上までスクロールされるようになります。

参考資料