Unity製アプリでiOSアプリのガイドライン遵守して! と言われてしまったら

どうしましょう。困りましたね。

普通のiOSアプリであればiOS標準のUIがガイドライン通りに(ある程度は)よしなに振舞ってくれるのですが、
Unityではガイドラインを理解して、各自で実装しなければなりません。

本ドキュメントでは、Unity製アプリでiOSガイドラインを遵守するため何をしなければいけないのかをまとめます。
ゲームというよりはどちらかというとツール系アプリ向けの記事となっています。

なおAndroidに関しては未考慮です。
また本ドキュメントの内容はApp Store審査の合格を保証するものではないのでご了承ください。

# iOS Human Interface Guidelines
ガイドラインはこちら。
https://developer.apple.com/design/human-interface-guidelines/ios/overview/themes/  

以降はガイドラインの章、重要項目の要約/対応方針、実装方法という構成でリストアップします。

# Visual Design

## Adaptivity and Layout
https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/  

 * マージンは8ptとする。  
 * タップエリアは最低44pt x 44ptとする。
 * `@1x`サイズでレイアウトを行い、デバイスごとのスケールファクターで拡大する。
 * 背景はSafeAreaの外を黒く塗りつぶすのではなく、デザイン要素をスクリーンいっぱいに広げる必要がある。  
つまり、ヘッダーや画像/動画、背景(余白)などはスクリーンいっぱいに広げる。
ただし、ボタンなどの操作系UIはSafeAreaに収める。  
    * iPhoneX系はノッチがあるためSafeAreaとスクリーン矩形が異なるため注意。

### 実装方法

Canvasの設定で簡単にできそうだぞ、と思いきやスクリーンサイズを元にオートレイアウトすると、
マージン8pt & 1〜3の整数倍のスケールが実現できません。
というのも、`@1x`のデバイスではマージン8pt=8px、`@2x`では16px、`@3x`では24pxが推奨ですが、
スクリーンサイズによるスケールでは20pxとか半端なマージンが発生してしまいます。

そこでデバイスのスケールファクターを取得してCanvasのCanvas Scalerコンポーネントに設定することで、
1〜3の整数倍にスケールするようにします。

UnityのAPIでスケールファクターを取得するものが見当たらなかっため、とてもシンプルなネイティブプラグインを書いてスケールファクターを取得しています。

該当箇所の抜粋ですが、たったこれだけ...。

ネイティブプラグイン側

```objc
extern "C" {
    float getScaleFactor() {
        return [UIScreen mainScreen].scale;
    }
}
```

Unity(C#)側

```C#
#elif UNITY_IOS
        scaleFactor = getScaleFactor();
#endif
        Canvas.scaleFactor = scaleFactor;
```

#### Canvas設定

Canvas Scalerコンポーネントをつけ、
UI Scale Mode: Constant Pixel Size とする。

また、Scale Factorコンポーネントをつける。
Scale FactorはiOS用のネイティブプラグインで、デバイスのスケールファクターを取得し、
Canvas ScalerのScale Factorを設定する。  

#### UIのサイズ設定
全てのUIのサイズは単位をptとして扱います。
つまり、`@1x`時のサイズを指定するということです。

こうしておくことで、Scale Factorコンポーネントで取得した倍率によって、  
各デバイスに適切なサイズに拡大されます。

ボタンや画像、フォントサイズなど(Canvas内の要素は)全てこの方法でスケールします。


#### ノッチ(iPhoneX系)対応

iPhoneX系ではSafeAreaがスクリーン矩形と異なります。

[Unity APIでSafeAreaを取得する](https://docs.unity3d.com/ScriptReference/Screen-safeArea.html)こともできますが、こちらのアセットが大変役立ちました。
大変ありがたいことです。
https://github.com/nkjzm/Unity-SafeAreaCanvas

また、2019.3からはシミュレーターが使用できます。
参考
http://tsubakit1.hateblo.jp/entry/2019/10/30/235150#Device-Simulator
https://unity.com/ja/releases/2019-2/programmer-tools#ios-and-android-improvements

#### 確認用のGame Sceneサイズ
いちいちデバイスへビルドしないと確認できないのではとても開発しにくいため、Game Sceneでデバイスのスクリーンをシミュレートします。

Unityでデフォルトで用意されているGame Sceneのサイズではなく、pt単位のサイズを設定して確認します。
これらは自分で設定しても良いですが、上記のアセットなどを使用すると自動で設定してくれます。
(とても便利!)

## Typography
https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/  

UnityプロジェクトにSFフォントを入れれば使用可能です。
ただしその分アプリサイズは大きくなるため、その点が許容できない場合はiOSのフォントを直接使う方法もあります。

## Video
https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/video/  

 * 動画の再生時はSafeAreaいっぱいではなく、スクリーンいっぱいに広げる。

[iPhone/iPad/Apple Watch解像度(画面サイズ)早見表](https://qiita.com/tomohisaota/items/f8857d01f328e34fb551)  

# Icons and Images
## Custom Icons
https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/custom-icons/  

* タブバーやナビゲーションバーなどUIパーツごとに適切なアイコンサイズが決まっている。

### 実装方法

各UIパーツ用のアイコン画像を`@3x`サイズで用意して使用します。
デバイスから取得したスケールファクターによって、`@1x`、`@2x`のデバイスではその分縮小して表示されます。  

または、SVGガイドラインの言及の通りにSVGを使う方法もあります。  
`@2x`サイズで用意し、SVGとしてUnityに取り込む。  
[Vector Graphics](https://github.com/Unity-Technologies/vector-graphics-samples)を使用すると、
GUIでSVG Imageが使用できるようになります。
SVGなので拡大しても綺麗なままです。  

他には、ScaleFactorから適切な画像をロードするコンポーネントを開発しても良いかもしれません。
工数との相談です。  

# Views
## Action Sheets
https://developer.apple.com/design/human-interface-guidelines/ios/views/action-sheets/  

 * キャンセル可能なこと。
 * 削除は強調すること。
 * 項目が多い場合はスクロール可能にすること。 

### 実装方法

他のUIの操作ができないように、SafeAreaだけでなく画面いっぱいに広がるブロッカー(Panel)を持つボタンのリストを作成します。
デザインによりけりなので、この実装が正解! というものはないです。

## Alerts
https://developer.apple.com/design/human-interface-guidelines/ios/views/alerts/  

 * アラート部分以外をタップしても閉じないこと。  
 * ホームボタンを押してアプリがバックグラウンドになった場合はアラートを非表示(キャンセル)にすること。

### 実装方法

Action Sheetsと同じく、他のUIを操作できない様にするブロッカーを作成します。

Unityアプリがバックグラウンドになったかどうかは[OnApplicationPause](https://docs.unity3d.com/ja/current/ScriptReference/MonoBehaviour.OnApplicationPause.html)で判定し、アラートを非表示にします。

## Popovers
https://developer.apple.com/design/human-interface-guidelines/ios/views/popovers/  

 * ポップオーバー表示時は他のUIは操作不可とする
 * ポップオーバー以外をタップした場合は閉じる
 * ポップオーバー内のアイテムをタップした場合は閉じる

(ちなみに、PopoversはiPhoneでは避け、代わりにモーダルを使うことを推奨とあるが...。)

### 実装方法

これもAction Sheetsと同じく、他のUIのタップをブロックするPanelを全画面に展開。
そのブロッカーをタップした場合はポップオーバーを閉じるようにします。

# Controls
## Page Controls 
https://developer.apple.com/design/human-interface-guidelines/ios/controls/page-controls/  

 * 複数のページから構成されるカルーセルのようなUIはページインジケーターを持つこと。

### 実装方法

こちらは、ガイドラインに従って愚直にページインジケーターを実装します。特に工夫する点はありませんが、
この様なUIを実現するアセットは調べれば色々ありそうです。(未調査...)

## Progress Indicators
https://developer.apple.com/design/human-interface-guidelines/ios/controls/progress-indicators/  

 * 保存データの読み込みなど時間のかかる処理は進捗を表示すること。

### 実装方法

こちらも特に工夫が必要というよりガイドラインを参考に愚直に実装します。  

## Text Fields & Extenstions Custom Keyboards
https://developer.apple.com/design/human-interface-guidelines/ios/controls/text-fields/  
https://developer.apple.com/design/human-interface-guidelines/ios/extensions/custom-keyboards/  

* 入力のためのヒント(プレースホルダー)を表示すること。
* 右端にクリアボタンを表示すること。
* パスワードなどセンシティブなデータの場合はセキュアなフォームを使用すること。
* 原則iOS標準のキーボードを使用し、カスタマイズが必要かどうかは熟考すること。

### 実装方法

正直なところUnityの文字入力機能はクセがあるといいますか、デフォルトだと以下の画像の様にキーボードの上にインプットがつきます。

![IMG_4A3716B20EB1-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11698/4e22c063-ab1a-85b1-b633-097688ee115e.jpeg)

このインプットは隠すこともできるのですが、隠すと色々と不思議な挙動をする(特に実機で)ことがあるので、素直にそのまま使用した方が開発者もユーザーも幸せになれると思います...。

また、UnityのInputTextコンポーネントの詳細設定についてはそれだけでいくつも記事が書けそうなので、
今回は割愛させていただきます。

# シミュレーターで確認したい

シミュレータで動作させるためには以下の設定を行う必要があります。

### Unity

Player Settings -> Other Settingのいくつかの設定を変更する必要があります。

- Auto Graphics APiのチェックを外し、Graphics APIにOpenGLES3かOpenGLES2を追加します。
<img width="363" alt="Unity設定１.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11698/4d32dee9-f938-93c1-8db2-3d8f908b0361.png">

- Taget SDKをSimulator SDKに変更する

<img width="354" alt="Unity設定２.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11698/7bc5093a-d3ae-a534-20fc-a6d3ebfc1363.png">

### Xcode

- ProjectのTargetsを選択し、Build Settingsを画像の通りにします。
- <img width="870" alt="XCode設定.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11698/35039137-2301-71e0-1067-f6bc1dac49f1.png">

# (おまけ)多言語対応

多言語対応に必要なファイルは大きく分けて3種類あります。

 * アプリで使用する多言語ファイル
 * iOSネイティブ側で表示するUI用の多言語ファイル(ネイティブプラグイン内でエラー時にアラートを表示する場合など)
 * iOSが表示する(カメラなどの)権限をユーザーに尋ねるダイアログ用の多言語ファイル

## アプリで使用する多言語ファイル

便利なアセットもありますが、自前で実装するのもそこまで大変ではないです。

参考 [Localizationのチュートリアル](https://learn.unity.com/tutorial/recorded-video-session-localization-tools?language=en#)

[SystemLanguage](https://docs.unity3d.com/ja/2019.2/ScriptReference/SystemLanguage.html)でiOSの言語設定を取得することができます。

## iOSネイティブ側で表示するUI用の多言語ファイル(ネイティブプラグイン内でエラー時にアラートを表示する場合など)

XcodeプロジェクトにLocalizable.stringsというファイルを設定する必要があります。
ビルド自動化については後述します。

## iOSが表示する(カメラなどの)権限をユーザーに尋ねるダイアログ用の多言語ファイル

XcodeプロジェクトにInfoPlist.stringsというファイルを設定する必要があります。
ビルド自動化については後述します。

# (おまけ)Xcode用のビルド自動化

UnityからiOS用にビルドするXcodeプロジェクトが吐き出されますが、
Xcodeの設定を自動化しておかないと、いちいち手動で設定しなければなりませんし、
Cloud Buildも有効活用できなくなるため、面倒でも自動化用のスクリプトを書きます。

基本的に(頑張って調べれば...)ほぼどんな設定でも自動化できると思います。
具体的なソースコードは今回は割愛させていただきますが、Linker Flagsを設定したりBitcodeをNOにしたり、その他Info.plistを変更するタイプの設定であれば比較的容易に書けます。

多言語対応のためLocalizable.strings、InfoPlist.stringsを設定する部分はかなりハマったのですが、
[UnityAppNameLocalizationForIOS](https://github.com/zeyangl/UnityAppNameLocalizationForIOS)というライブラリにお世話になりました。

また、Capabilitiesの設定もハマったのでメモしておきます。
こちらの方の投稿が大変役立ちました。ありがとうございます。
[Unity5のPostProcessBuildでXcode Capabilityの設定する方法](https://qiita.com/fullcorder/items/b82c48022acd0b4ff957)