Adaptive UI Designについてのまとめ（Unified Storyboards, Size Classes, etc...）

この記事は、WWDC2014で発表された[Building Adaptive Apps with UIKit](http://devstreaming.apple.com/videos/wwdc/2014/216xxcnxc6wnkf3/216/216_building_adaptive_apps_with_uikit.pdf?dl=1)を元に、以下の項目についてまとめています。

* Adaptive UIについてのざっくりとしたまとめ
  * Size Class 
  * Traits
  * Unified Storyboards
* Interface Builderを使った簡単なサンプル
  * Size Classごとに画面のレイアウトを変えてみる（Size Classに応じて適用されるConstraintsを変える）

以下の項目については、この記事では触れません。別な記事で説明します。
* Appearance Proxy
* Split View Controller

# 前提知識
## Adaptive UI Design
解像度や画面サイズの異なるデバイスに対応するため、iOS8以降はAdaptive User Interfaceという考え方が取り入れられました。

ざっくりといえば、iOS6以降で導入されたAuto Layoutや、iOS8以降で導入されたSize ClassesやTraitsなどを組み合わせることにより、複数のデバイスに対応したレイアウトを作成するということを意図しているようです。

### Auto Layout
詳しい資料はたくさんあるので、ここでは割愛します………

### Size Classes
異なる解像度のデバイスの画面サイズは、*Size Classes*にて管理されます。
デバイスの画面サイズや解像度、デバイスの向きに応じて、Compact/Regular、Horizontal/Verticalというように分類しています。

![sizeClasses.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/4ffa6a74-a283-714b-8a31-61870d0e933c.jpeg)

### Traits
*Size Classes*等の、現在のデバイスの情報は```UITraitCollection```というクラスで管理されています。
```UITraitCollection```から、以下の情報を知ることができます。
* HotizontalのSize Class
* VerticalのSize Class
* iPhone/iPadのどちらのデバイスで表示しているか
* 画面の倍率

## Unified Storyboards
これまで説明してきた*Auto Layout*、*Size Classes*や*Traits*を組み合わせることにより、1つのUniversal Storyboardで**iPhone(4s〜6)/iPhone 6 Plus/iPadなどの異なるデバイスやportlait/landscapeに応じたレイアウトを指定する** ことができるようになります。
具体的には、*Size Class*に応じた*Constraints*を指定することにより、1つのStoryboardでも複数デバイスで破綻しないレイアウトを実現することができます。

# StoryboardだけでiPhone/iPadのレイアウトを変更してみる
仕組みの話だけを追っていても何ができるのかわからないので、実際に手を動かしてみましょう。

iPhone（Portrait）とそれ以外の場合に、以下の通りレイアウトを変えるサンプルを作成してみます。
![design.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/cda7d1dc-4567-af20-56a3-fb4a3f6eedc5.jpeg)

ソース一式は[こちら](https://github.com/nomadmonad/SizeClassesSampleWithUniversalStoryboards)。

## Universal Device向けのプロジェクト作成
はじめに、何でも良いので適当なプロジェクトを作成します。
今回はiPhone/iPadの両方で使いたいので、"Device: "には```Universal```を指定します。

![project.png](https://qiita-image-store.s3.amazonaws.com/0/30970/29daed69-b0b8-709a-ab30-6df3d2f17029.png)

## どのSize Classでも画面の中央に表示されるUILabelを追加する
View Controllerの中心部に、UILabelを配置します。

Horizontal/Verticalの```Center In Controller```に、0を設定してConstraintsを追加します。

![horizontal_vertical_center.png](https://qiita-image-store.s3.amazonaws.com/0/30970/7c0f222f-1747-3328-b041-39ffd0fb380b.png)

追加まで終わったら、動作確認してみます。
デバイス（iPhone/iPad）やPortrait/Landscapeを切り替えても、画面の中心にLabelが表示されます。

## 横一列に並ぶUIButtonを追加する
次に、UIButtonをLabelの下で横一列に並ぶよう配置します。

配置後、適当にConstraintsを設定してください。

### Constraintsに、iPhone(Landscape)、iPadでのみ有効になる設定を追加する
次に、iPhone(Landscape)・iPadで表示した時(Size ClassがWidth: Regular Height: Any)のみ横一列に並ぶよう、Constraintsの設定を変更します。
追加したConstraintsを選択した状態で、Size Inspectorの中にあるSize Classで、以下の通り操作します。

0. デフォルトの行（何の説明もなく、Installedのチェックが付いている行）のチェックを外す
0. ```＋```ボタンを押す
0. 表示された"Add Size Class Customization"で、```Regular Width -> Any Height```を選択する

![addConstraint_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/5b8a66b7-1635-ebd9-5008-fd26b673c9ad.jpeg)
![addConstraint_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/e91798e9-3cac-5c47-107f-4132c378bc9d.jpeg)


UIButton配置時に追加したConstraints全てに対し、同じように操作してください。

この状態で動作確認すると、iPhone（Landscape）とiPadの場合のみ、追加したボタンが横一列に並んで表示されます。

## iPhone(Landscape)のSize Classが指定された時に、UIButtonが縦に並ぶようConstraintsを追加する
次に、iPhone(Portrait)の時にはUIButtonが画面の中心で縦一列に並ぶようConstraintsを追加します。

左のUIButtonを選択し、Horizontal Center In Controller: 0のConstraintsを追加してください。
追加後、先ほどと同じように、Size Inspectorを使って```Compact Width -> Any Height```の時に有効となる条件を追加します。
もうひとつのUIButtonについても、同じようにHorizontal Center In Controller: 0で、```Compact Width -> Any Height```のSize Classで有効になるConstraintsを追加してください。
また、x方向のConstraintsも忘れずに追加してください。

この状態で動作確認すると、iPhone(Portrait)で、UIButtonが縦一列に並んで表示されます。

## Size Class Controlでレイアウトを確認する
ここまで操作した内容は、Interface BuilderのSize Class Controlから確認することもできます。

試しに、iPhone(Portrait)の状態でどのようにConstraintが適用されるか確認してみましょう。
InterfaceBuilderでView Controllerを選択し、画面下部にあるSize Class Control(w Any|h Anyなどで表示されている)で、```Compact Width |Any Height```となるよう領域を選択します。
選択されると、Size Classが"Compact width/Any Height"の状態でどのようなConstraintsが有効になっているか確認することができます。
![sizeClassControl_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/f1da3772-4ff3-d793-5123-088e7e195271.jpeg)
![sizeClassControl_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/40f001bd-4397-7c43-cb9a-2ea970b12c9c.jpeg)
![sizeClassControl_3.jpg](https://qiita-image-store.s3.amazonaws.com/0/30970/cdbfad60-b557-63fc-4e4f-a0ee559e9d02.jpeg)

同じようにSize Classを変更することで、iPhone(Landscape)などの状態も見ることができます。

# 気になった点
## Auto Layoutの知識が前提となる
基本的に、Constraintsの使い方が肝になりますのでAuto Layoutの知識が必要になります。
あらかじめ資料を読むなど、事前知識がある方が理解しやすいです。

## Constraintsの管理が大変そう
同じ要素に対して複数のConstraintsを設定することになるので、どの部品に対するものなのか、どの条件のときに有効となるのか、非常にわかりづらくなります。
いつ、何に対して有効となるConstraintsなのか、はっきりとわかる名前をつけるなどの方法でカバーする方が良いと思います。

## iPhone デバイス間の解像度・画面サイズ差の吸収方法
Size ClassやTraitsを使えば、iPhone/iPadのデバイス差は検知できます。
ただ、```UserInterfaceIdeom```はiPhone/iPadの２種類、```UIUserInterfaceSizeClass```はUnspecified/Compact/Regularの3種類しかありません。
これらの情報からはiPhoneのデバイス差(4s/5系/6/6Plus)を検知できないので、別な方法で検知する必要があります。

## iOS7 後方互換性の確保
[Appleのドキュメント](https://developer.apple.com/library/ios/recipes/xcode_help-IB_adaptive_sizes/chapters/DeployingSizeClassesonEarlieriOSVersions.html#//apple_ref/doc/uid/TP40014436-CH13-SW1)に記載されているとおり、以下のような条件が満たされればiOS7でもSize Classesを指定したStoryboardを使うことができます。

* アプリがXcode6以降でビルドされた
* deployment targetがiOS8以前
* storyboardやxibでSize Classが指定されている
* Size ClassのHeightがCompactではない

ただし、Compact heightのものは適用されないと記載されていますので、iPhone(Portrait)の場合には適用されません。
また、iOS7では[nibにSize Classが適用されない](http://openradar.appspot.com/18737656)という不具合が存在しているようです。

いずれにせよ、iOS7で適用するときは

* iPhoneではPortraitの画面を前提に適用する
* nibはSize Classに依存しないよう実装する

などの注意を払って実装するほうが良さそうです。

# 参考にしたURL
## WWDC 2014

[What's New in Cocoa Touch](http://devstreaming.apple.com/videos/wwdc/2014/202xx3ane09vxdz/202/202_whats_new_in_cocoa_touch.pdf?dl=1)
[Building Adaptive Apps with UIKit](http://devstreaming.apple.com/videos/wwdc/2014/216xxcnxc6wnkf3/216/216_building_adaptive_apps_with_uikit.pdf?dl=1)
[What's New in Interface Builder](http://devstreaming.apple.com/videos/wwdc/2014/411xx0xo98zzoor/411/411_whats_new_in_interface_builder.pdf?dl=1)

## Apple Developer Library
[What's New In iOS: iOS8](https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html)
[iOS Human Interface Guidelines Adaptivity and Layout](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/LayoutandAppearance.html)
[Deploying an App With Size Classes on Earlier iOS Versions](https://developer.apple.com/library/ios/recipes/xcode_help-IB_adaptive_sizes/chapters/DeployingSizeClassesonEarlieriOSVersions.html#//apple_ref/doc/uid/TP40014436-CH13-SW1)
[AdaptivePhotos: An Adaptive Application](https://developer.apple.com/library/prerelease/ios/samplecode/AdaptivePhotos/Introduction/Intro.html)

## Others
[iMore: Adaptive UI in iOS 8: Explained](http://www.imore.com/adaptive-ui-ios-8-explained)
[Beginning Adaptive Layout Tutorial](http://www.raywenderlich.com/83276/beginning-adaptive-layout-tutorial)
[Nerdely: Overcoming the iPad Tax With iOS 8 and Adaptive UI](http://blog.nerdery.com/2014/08/overcoming-ipad-tax-ios-8-adaptive-ui/)
[Xamarin: Introduction to Unified Storyboards](http://developer.xamarin.com/guides/ios/platform_features/intro_to_unified_Storyboards/)
[stackoverflow: Design app that supports both iOS8 and iOS7](http://stackoverflow.com/questions/26293829/design-app-that-supports-both-ios8-and-ios7)
[stackoverflow: Xcode6 Universal Storyboards and view sizing](http://stackoverflow.com/questions/24377035/xcode6-universal-storyboards-and-view-sizing)
[stackoverflow: Xcode 6 – Unified storyboard & Size Classes](http://stackoverflow.com/questions/24075597/xcode-6-unified-storyboard-size-classes)
[stackoverflow: How can Xcode 6 adaptive UIs be backwards-compatible with iOS 7 and iOS 6?](http://stackoverflow.com/questions/24172860/how-can-xcode-6-adaptive-uis-be-backwards-compatible-with-ios-7-and-ios-6)
[stackoverflow: Example for iPhone portrait landscape adaptive UI which is backwards-compatible with iOS 7](http://stackoverflow.com/questions/26694627/example-for-iphone-portrait-landscape-adaptive-ui-which-is-backwards-compatible)])