Xcode
iOS
Swift
ios11
iPhoneX

これからの「iPhoneX」の話をしよう。 - UITableView、UICollectionView編

More than 1 year has passed since last update.

はじめに

この投稿はAbemaTV Advent Calendar 2017 14日目の記事です!

今回は、現在AbemaTV iOSチームのメンバーの一人であるcokaholicこと、辰己が担当します。

以前、【随時更新】iPhoneX完全対応マニュアル という投稿を書いたのですが、その中では触れられていない、これから新規でiOSアプリを開発する方のための具体的なiPhoneX対応AutoLayoutの使い方をご紹介しようと思います。
今回は普段よく使うUITableView編UICollectionView編です!

UITableView編

まずは、UITableViewのStoryboard上におけるAutoLayoutの使い方について話そうと思います。

Xcode9からはStoryboard内でUIViewControllerを作成すると、デフォルトで Use Safe Area Layout Guides にチェックが入っており、初めから SafeArea (UILayoutGuide) が追加されています。

UseSafeAreaLayoutGuides

そして、適当に中身を見繕ったUITableViewに、以下のように SafeAreaの各Edgeに対して制約を追加した場合SuperView(今回はUIViewControllerのview)の各Edgeに対して制約を追加した場合 だと、どのような見た目になるでしょうか?

SafeAreaの各Edgeに対して制約を追加 SuperViewの各Edgeに対して制約を追加
SafeArea SuperView

ちなみに、 SuperViewへの制約の追加方法は、SafeAreaに制約を追加した後、追加した制約のFirstItemをSafeAreaからSuperViewに変更すれば適用することができます。
スクリーンショット 2017-12-14 3.46.38.png

SafeAreaの各Edgeに対して制約を追加した場合

Portrait Landscape
tate yoko

SafeAreaに対して制約を追加した場合、UITableViewのフレームサイズはSafeArea内に収まります。
そのため、Portraitの際には上:44pt、下:34pt、Landscapeの際は左右:44pt, 下:21pt がフレーム外となり、切れて表示されます。
しかし、これでは見た目が悪すぎますし、iPhoneXの凹みを活かしきれていないレイアウトになってしまっています。

SuperViewの各Edgeに対して制約を追加した場合

Portrait (Top) Landscape (Top)
tate yoko
Portrait (Bottom) Landscape (Bottom)
tate yoko

続いて、SuperViewであるUIViewControllerのviewの各Edgeに対して制約を追加した場合、UITableViewのフレームサイズは画面全体に広がります。
ですが、各画面に注目すると、 上部と下部にセーフエリア分のinset が設けられていることがわかります。
これは、UITableViewの親クラスであるUIScrollViewの contentInsetAdjustmentBehavior プロパティによるものです。
デフォルトのAutomaticを指定しておけば、Viewの各Edgeに制約を追加した際にそのViewのsafeAreaInsetsの値に応じて自動で上下にセーフエリア分のinsetを追加してくれます。
Storyboard上とコード上で指定する場合はそれぞれ以下のようになります。

Storyboard上で指定する場合

UseSafeAreaLayoutGuides

コード上で指定する場合

// iOS11からしか指定できないので、iOS10以下もサポートする場合は分岐を追加
if #available(iOS 11, *) {
    tableView.contentInsetAdjustmentBehavior = .automatic
}

さらに、Landscapeの際の画面に注目すると、 UITableViewのセルの左右にセーフエリア分のinset が設けられていることがわかります。
これは、UITableViewの insetsContentViewsToSafeArea プロパティによるものです。
insetsContentViewsToSafeAreaを有効にしておけば、Viewの各Edgeに制約を追加した際にそのViewのsafeAreaInsetsの値に応じて自動でセルの左右にセーフエリア分のinsetを追加してくれます。
Storyboard上とコード上で指定する場合はそれぞれ以下のようになります。

Storyboard上で指定する場合

UseSafeAreaLayoutGuides

コード上で指定する場合

// iOS11からしか指定できないので、iOS10以下もサポートする場合は分岐を追加
if #available(iOS 11, *) {
    tableView.insetsContentViewsToSafeArea = true
}

UICollectionView編

続いて、UICollectionViewのStoryboard上におけるAutoLayoutの使い方について話そうと思います。

適当に中身を見繕ったUICollectionViewに、先ほどと同じく以下のように SafeAreaの各Edgeに対して制約を追加した場合SuperView(今回はUIViewControllerのview)の各Edgeに対して制約を追加した場合 だと、どのような見た目になるでしょうか?

SafeAreaの各Edgeに対して制約を追加 SuperViewの各Edgeに対して制約を追加
SafeArea SuperView

SafeAreaの各Edgeに対して制約を追加した場合

Portrait Landscape
tate yoko

SafeAreaに対して制約を追加した場合、UICollectionViewのフレームサイズはSafeArea内に収まります。
そのため、Portraitの際には上:44pt、下:34pt、Landscapeの際は左右:44pt, 下:21pt がフレーム外となり、切れて表示されます。
またしてもiPhoneXの凹みを活かしきれていないレイアウトになってしまっています。

SuperViewの各Edgeに対して制約を追加した場合

Portrait (Top) Landscape (Top)
tate yoko
Portrait (Bottom) Landscape (Bottom)
tate yoko

続いて、SuperViewであるUIViewControllerのviewの各Edgeに対して制約を追加した場合、UICollectionViewのフレームサイズは画面全体に広がります。
ですが、各画面に注目すると、 上部と下部にセーフエリア分のinset が設けられていることがわかります。
これも先ほどと同じく、UICollectionViewの親クラスであるUIScrollViewの contentInsetAdjustmentBehavior プロパティによるものです。

しかし、今回はUITableViewの場合と比べて、Landscapeの表示に大きな違いがあります。
そうです、Landscape時のcontentViewに対する左右のセーフエリアマージン が無いのです。
これでは左右のマージン分はみ出してしまう...美しくない...

でもご安心を。ちゃんと対応する方法は用意されています!

解決方法

具体的な解決方法としましては、UICollectionViewFlowLayoutの sectionInsetReference プロパティを変更することで解決します。
まず、Storyboard上では指定できないため、以下のようにIBOutletでUICollectionViewFlowLayoutを接続します。

Bind

次に、接続したflowLayoutを以下のように書き換えましょう。
ここのdidSet内にて、sectionInsetReference.fromSafeArea を指定することで完了です。

@IBOutlet weak var flowLayout: UICollectionViewFlowLayout! {
    didSet {
        // iOS11からしか指定できないので、iOS10以下もサポートする場合は分岐を追加
        if #available(iOS 11, *) {
            flowLayout.sectionInsetReference = .fromSafeArea
        }
    }
}

実行してみると、以下のように無事にcontentViewの左右にセーフエリアマージンができていることがわかります。

スクリーンショット 2017-12-14 14.53.12.png

まとめ

今回はここまでです!
UITableViewもUICollectionViewも基本はSuperViewの各Edgeに対して制約を貼ることで、iPhoneXでも違和感の無い、凹みを活かしたレイアウトが可能です。

最後に、私はiPhoneX対応をがっつり担当したのですが、普段はiPhone8を使用していますw
CPUも高速になり、反応速度も大幅に向上したTouchIDはすごく便利ですね(ニッコリ)

以上です!
最後まで読んでいただき、ありがとうございました!

参考リンク