【iOS】2018年、Size Class のバリエーションが複雑になってきた

## はじめに（TL;DR）

2018 年 11 月に発売された 第３世代 iPad Pro 12.9 インチ。
マルチタスクで画面分割すると Size Class が他のデバイスと異なるみたいです。

- **第３世代 iPad Pro 12.9" はポートレートで 2/3画面分割すると Horizontal Size Class が Regular**
- **その他のデバイスはポートレートで 2/3画面分割すると Horizontal Size Class が Compact**

<img width="500" alt="UISplitViewController DisplayMode??.002.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/63842/6cce9ee0-b1c9-bec5-1211-210bd160b944.jpeg">

アダプティブレイアウトを実現しているアプリでは十分に注意したいですね。
特に `UISplitViewController` は Horizontal Size Class が Regular の場合のみ画面分割します。

つまり、 **iPad Pro 12.9" は、第３世代デバイスとそれ以前のデバイスでレイアウトが異なる場合がある**ことを意味しています。

多くの Apple 純正アプリでレイアウトの違いを確認できます。
シミュレータで連絡先アプリを Split View 表示してみると、下記のような表示になります。

**第２世代 iPad Pro 12.9"**

<img width="600" alt="スクリーンショット 2018-12-02 13.51.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/99a30b2c-a84f-43c7-2ea5-ede4aa94cc78.png">

**第３世代 iPad Pro 12.9"**

<img width="600" alt="スクリーンショット 2018-12-02 13.54.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/d21b4586-b8ba-5f08-56a9-0b6b8ecd6dc2.png">

**第３世代 iPad Pro では Side-by-side なレイアウトで表示されます。**
これらの点を十分考慮して、アプリを設計していきたいですね。

ということで、この記事で抑えておきたい内容は以上になりますが、
他のデバイスの Size Class についても触れておきます。

### 検証環境

- macOS： バージョン 10.14.1
- Xcode： Version 10.1
- Swift： Apple Swift version 4.2.1

## Apple のドキュメントはまだ更新されていない

Size Class のバリエーションは Human Interface Guidelines の [Adaptivity and Layout](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/#size-classes) のページに記載があります。
このページに記載されている内容を理解しておけば、基本的には間違いが無いので良いです。

ただし、2018 年 12 月現在、Human Interface Guidelines には、
第３世代 iPad Pro 12.9" Portrait 2/3　Split View についての記載がありません。
この点だけ注意が必要です。

## Size Class 一覧

Apple のドキュメントが未整備なので、2018年現在の Size Class について整理しました。

### デバイスの Size Class

2018年現在、大きく３つに分類することができます。

- iPad/iPad Pro
- iPhone (6|7|8) plus, iPhone XS Max/XR
- 上記以外の画面サイズの iPhone

#### iPad/iPad Pro

サイズ、向きに依らず、すべてのデバイスで共通の Size Class です。

#### iPhone

Portrait はすべてのデバイスで共通の Size Class です。
Landscape は plus シリーズ / XS Max/ XR のみ水平向が Regular です。

#### 一覧表

表にすると次のようになります。

<img width="500" alt="UISplitViewController DisplayMode??.001.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/63842/88cf8580-da07-51c9-a846-c9dabc92a635.jpeg">

#### プログラムから取得する際の注意点

プログラムからデバイスの Size Class を取得する場合は、`UIScreen.main.traitCollection` から取得できます。

`UIView` から取得できる TraitCollection は、マルチタスクや UISplitViewController の影響を受けるため、
デバイスの Size Class と異なる値が設定される場合があるので注意しましょう。

また、Size Class の変更は `UIViewController` の [`traitCollectionDidChange(_:)`](https://developer.apple.com/documentation/uikit/uitraitenvironment/1623516-traitcollectiondidchange) により検知できます。

余談になりますが、 `traitCollectionDidChange(_:)` は画面サイズの変更や画面の回転を検知することに関しては一般的には不向きです。

上記一覧表の通り、iPad の場合は画面向きによらずに Regular / Regular です。
フルスクリーンモードでデバイスを回転した場合は、TraitCollection の変更が発生しないため、`traitCollectionDidChange(_:)` が呼ばれないことに注意しましょう。

### マルチタスクの Size Class

iPad では、Split View によるマルチタスクが可能です。
マルチタスク時は分割比率に応じて Size Class が決まります。

#### 【New!】 Portrait の Split View

Portrait で Split View にすると、水平向は Compact になります。
ただし、第３世代の iPad Pro 12.9" だけは、2/3 Split View で水平向が Regular です。
第３世代 iPad Pro 11" は Compact です。

<img width="500" alt="UISplitViewController DisplayMode??.003.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/63842/b99c8d46-6e4d-aad8-8a18-8fa3e0878aba.jpeg">

#### Landscape の Split View

Landscape では、Portrait とは異なり、1/2 Split View が可能です。
iPad Pro 12.9" とそれ以外のデバイスで 1/2 Split View の SizeClass が異なります。

<img width="500" alt="UISplitViewController DisplayMode??.004.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/63842/a3cdc41c-e379-ea93-b5ae-e39591cea494.jpeg">

シミュレータで連絡先アプリ、メッセージアプリを Split View 表示してみると、下記のような表示になります。

**第２世代 iPad Pro 10.5"**

<img width="500" alt="スクリーンショット 2018-12-02 18.31.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/d0941d73-9e9c-0102-eb69-e55cd2214cb3.png">

<img width="500" alt="スクリーンショット 2018-12-02 18.31.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/a2b38a15-38a7-4d89-3c4d-9c4ef3be5437.png">

**第２世代 iPad Pro 12.9"**

<img width="500" alt="スクリーンショット 2018-12-02 15.17.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/54b080f3-d5cb-057e-8087-9b067fe8181f.png">

<img width="500" alt="スクリーンショット 2018-12-02 15.22.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/74b69506-1cd1-59c1-7f27-b62d16a370dd.png">


## 【余談】実際の分割比率（面積比）

各デバイスの Split View のスクリーンショットを取得する過程で気付いたのですが、
1/3, 2/3 Split View は面積比が 1/3, 2/3 というわけではないみたいです。
デバイス毎に異なっているように見えます。

次のサンプルプロジェクトとシミュレータを用いて、
どのくらいの比率、サイズになるのかを確認してみました。

### サンプルプロジェクトについて

サンプルプロジェクトは [GitHub](https://github.com/takehito-koshimizu/SizeClassSample) に公開しています。
Build Scheme は３つ用意していて、用途は下記の通りです。
実験する場合は、同一デバイスに対して３つともビルドします。

| スキーム名 | 用途 |
|:--|:--|
| SizeClassSample1 | 画面の幅、スクリーンに対する表示割合、Horizontal Size Class を表示します |
| SizeClassSample2 | SizeClassSample1 と同様の機能でバンドル ID が異なります |
| SizeClassSample3 | UISplitViewController の子VC の表示割合などを表示します |

SizeClassSample2 が SizeClassSample1 と同様の機能でバンドル ID が異なるのは、別アプリとして起動させて分割比率を確認するためです。

### Portrait

**12.9" iPad Pro（ 第３世代 ）**

<img width="400" alt="スクリーンショット 2018-12-02 19.24.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/3e21df25-d991-1d52-3784-c88b943d69cc.png">

**12.9" iPad Pro**

<img width="400" alt="スクリーンショット 2018-12-02 19.22.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/3a4f2372-45ce-4802-1971-b226176df9d8.png">

**11" iPad Pro**

<img width="400" alt="スクリーンショット 2018-12-02 19.20.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/b1a70c2b-c9f0-00c0-4a52-8bfd2d5988fd.png">

**10.5" iPad Pro**

<img width="400" alt="スクリーンショット 2018-12-02 19.18.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/502bb4d7-60a5-6a8f-08f6-746fdb33125e.png">

**9.7" iPad**

<img width="400" alt="スクリーンショット 2018-12-02 19.15.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/78bb6147-7385-2eac-c68b-5c08068b7aea.png">

### Landscape

**12.9" iPad Pro（ 第３世代 ）**

<img width="500" alt="スクリーンショット 2018-12-02 19.24.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/79722649-9a7a-64b1-0ef3-fb3495656f2d.png">

**12.9" iPad Pro**

<img width="500" alt="スクリーンショット 2018-12-02 19.22.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/9dc7216f-6f29-25fa-ba33-d4295a29a8a7.png">

**11" iPad Pro**

<img width="500" alt="スクリーンショット 2018-12-02 19.20.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/7e2ac23b-a17d-fcb6-546a-dae3f283edd9.png">

**10.5" iPad Pro**

<img width="500" alt="スクリーンショット 2018-12-02 19.18.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/6931c52d-edd2-88bf-ab53-f913e0ccb373.png">

**9.7" iPad**

<img width="500" alt="スクリーンショット 2018-12-02 19.14.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/9390a29c-23cd-10aa-b7be-b5c18cbd6ed2.png">

## SplitViewController の Size Class

[`UISplitViewController`](https://developer.apple.com/documentation/uikit/uisplitviewcontroller) は Horizontal Size Class が Regular の場合に限り、
プライマリビューコントローラとセカンダリビューコントローラ（ 以下、プライマリとセカンダリ ）を Side-by-side に表示することができます。
（ `preferredDisplayMode` でプライマリの表示方法を Side-by-side 以外に変えることは可能 ）

Side-by-side で表示した場合のプライマリとセカンダリの Horizontal Size Class は下記の通りです。

|ビューコントローラ|Horizontal Size Class|
|:--|:--|
|プライマリビューコントローラ|Compact|
|セカンダリビューコントローラ|Regular|

第３世代 iPad Pro 12.9" では下記スクリーンショットのような表示になりました。 

**Portrait**

<img width="400" alt="スクリーンショット 2018-12-02 16.11.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/933381d6-91fb-31ed-3d0f-ea5ee2df645a.png">

**Landscape**

<img width="500" alt="スクリーンショット 2018-12-02 16.11.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/110e7f55-a246-ce55-6320-97b8cbc41917.png">

分割幅はデフォルトの状態のものです。
下記のプロパティを設定することでプライマリの分割幅を調整できます。

- `preferredPrimaryColumnWidthFraction`
- `minimumPrimaryColumnWidth`
- `maximumPrimaryColumnWidth`

デフォルトでは、各プロパティには `UISplitViewController.automaticDimension` が設定されていて、システムが適当な大きさを割り当てます。

また、セカンダリの Size Class が Regular なので、セカンダリに `UISplitViewController` を更に入れ子することで、３ペインのような画面構成をつくることも可能です。[^1]

**上述の Size Class は、Side-by-side なレイアウトであれば、マルチタスク時も成立します。**
分割幅はプライマリ側の値で決定されるので、**セカンダリよりもプライマリの方が大きな画面サイズになる**ことがあります。

`UISplitViewController.automaticDimension` な状態でマルチタスク表示した場合のスクリーンショットを載せておきます。

**第３世代 iPad Pro 12.9"**

<img width="500" alt="スクリーンショット 2018-12-02 16.06.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/63842/68d4b190-2554-d947-12e9-921bfaad6566.png">

ということで、**セカンダリが最も画面幅が狭いにもかかわらず、Horizontal Size Class が Regular** になりました。
この挙動が気になるようであれば、`UISplitViewController` に対して下記の調整をすると良いでしょう。

- `preferredDisplayMode` で表示方法を変える
- `preferredPrimaryColumnWidthFraction` などでプライマリの横幅を調整する

## まとめ

- 第３世代 iPad Pro 12.9" でマルチタスクをすると Size Class が他と異なる場合がある
- 1/3, 2/3 Split View はデバイスごとに面積比が違う
- SplitViewController を使用しているとかなりスリムな画面になる場合がある
  - DisplayMode を変更するか、プライマリの幅を調整する

## 参考

- [Adaptivity and Layout - Visual Design - iOS - Human Interface Guidelines - Apple Developer](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/)
- [Adopting Multitasking Enhancements on iPad: Getting Oriented](https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/AdoptingMultitaskingOniPad/index.html#//apple_ref/doc/uid/TP40015145-CH3-SW1)
- [UISplitViewController - UIKit | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uisplitviewcontroller)


[^1]: [Multiple UISplitViewController Tutorial | raywenderlich.com](https://www.raywenderlich.com/7212-multiple-uisplitviewcontroller-tutorial) に実装例とサンプルコードがあります。