はじめに
この投稿は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) が追加されています。
そして、適当に中身を見繕ったUITableViewに、以下のように SafeAreaの各Edgeに対して制約を追加した場合 と SuperView(今回はUIViewControllerのview)の各Edgeに対して制約を追加した場合 だと、どのような見た目になるでしょうか?
SafeAreaの各Edgeに対して制約を追加 | SuperViewの各Edgeに対して制約を追加 |
---|---|
ちなみに、 SuperViewへの制約の追加方法は、SafeAreaに制約を追加した後、追加した制約のFirstItemをSafeAreaからSuperViewに変更すれば適用することができます。
SafeAreaの各Edgeに対して制約を追加した場合
Portrait | Landscape |
---|---|
SafeAreaに対して制約を追加した場合、UITableViewのフレームサイズはSafeArea内に収まります。
そのため、Portraitの際には上:44pt、下:34pt、Landscapeの際は左右:44pt, 下:21pt がフレーム外となり、切れて表示されます。
しかし、これでは見た目が悪すぎますし、iPhoneXの凹みを活かしきれていないレイアウトになってしまっています。
SuperViewの各Edgeに対して制約を追加した場合
Portrait (Top) | Landscape (Top) |
---|---|
Portrait (Bottom) | Landscape (Bottom) |
---|---|
続いて、SuperViewであるUIViewControllerのviewの各Edgeに対して制約を追加した場合、UITableViewのフレームサイズは画面全体に広がります。
ですが、各画面に注目すると、 上部と下部にセーフエリア分のinset が設けられていることがわかります。
これは、UITableViewの親クラスであるUIScrollViewの contentInsetAdjustmentBehavior プロパティによるものです。
デフォルトのAutomatic
を指定しておけば、Viewの各Edgeに制約を追加した際にそのViewのsafeAreaInsets
の値に応じて自動で上下にセーフエリア分のinsetを追加してくれます。
Storyboard上とコード上で指定する場合はそれぞれ以下のようになります。
Storyboard上で指定する場合
コード上で指定する場合
// iOS11からしか指定できないので、iOS10以下もサポートする場合は分岐を追加
if #available(iOS 11, *) {
tableView.contentInsetAdjustmentBehavior = .automatic
}
さらに、Landscapeの際の画面に注目すると、 UITableViewのセルの左右にセーフエリア分のinset が設けられていることがわかります。
これは、UITableViewの insetsContentViewsToSafeArea プロパティによるものです。
insetsContentViewsToSafeArea
を有効にしておけば、Viewの各Edgeに制約を追加した際にそのViewのsafeAreaInsets
の値に応じて自動でセルの左右にセーフエリア分のinsetを追加してくれます。
Storyboard上とコード上で指定する場合はそれぞれ以下のようになります。
Storyboard上で指定する場合
コード上で指定する場合
// 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の各Edgeに対して制約を追加した場合
Portrait | Landscape |
---|---|
SafeAreaに対して制約を追加した場合、UICollectionViewのフレームサイズはSafeArea内に収まります。
そのため、Portraitの際には上:44pt、下:34pt、Landscapeの際は左右:44pt, 下:21pt がフレーム外となり、切れて表示されます。
またしてもiPhoneXの凹みを活かしきれていないレイアウトになってしまっています。
SuperViewの各Edgeに対して制約を追加した場合
Portrait (Top) | Landscape (Top) |
---|---|
Portrait (Bottom) | Landscape (Bottom) |
---|---|
続いて、SuperViewであるUIViewControllerのviewの各Edgeに対して制約を追加した場合、UICollectionViewのフレームサイズは画面全体に広がります。
ですが、各画面に注目すると、 上部と下部にセーフエリア分のinset が設けられていることがわかります。
これも先ほどと同じく、UICollectionViewの親クラスであるUIScrollViewの contentInsetAdjustmentBehavior プロパティによるものです。
しかし、今回はUITableViewの場合と比べて、Landscape
の表示に大きな違いがあります。
そうです、Landscape時のcontentViewに対する左右のセーフエリアマージン が無いのです。
これでは左右のマージン分はみ出してしまう...美しくない...
でもご安心を。ちゃんと対応する方法は用意されています!
解決方法
具体的な解決方法としましては、UICollectionViewFlowLayoutの sectionInsetReference プロパティを変更することで解決します。
まず、Storyboard上では指定できないため、以下のようにIBOutletでUICollectionViewFlowLayout
を接続します。
次に、接続したflowLayout
を以下のように書き換えましょう。
ここのdidSet内にて、sectionInsetReference
に .fromSafeArea を指定することで完了です。
@IBOutlet weak var flowLayout: UICollectionViewFlowLayout! {
didSet {
// iOS11からしか指定できないので、iOS10以下もサポートする場合は分岐を追加
if #available(iOS 11, *) {
flowLayout.sectionInsetReference = .fromSafeArea
}
}
}
実行してみると、以下のように無事にcontentViewの左右にセーフエリアマージンができていることがわかります。
まとめ
今回はここまでです!
UITableViewもUICollectionViewも基本はSuperViewの各Edgeに対して制約を貼ることで、iPhoneXでも違和感の無い、凹みを活かしたレイアウトが可能です。
最後に、私はiPhoneX対応をがっつり担当したのですが、普段はiPhone8を使用していますw
CPUも高速になり、反応速度も大幅に向上したTouchIDはすごく便利ですね(ニッコリ)
以上です!
最後まで読んでいただき、ありがとうございました!