Objective-C
C#
iOS
Unity
ARKit

Unity+iOSでカメラロールにスクショを保存するまで

はじめに

初qiitaです。
Unityで作成するARKitアプリに「カメラで撮影する」機能を入れようと思ったら、思いの外情報が少なかったので、いろいろ調べて実装しました。
iOSネイティブを書くことはほとんど無かったのでミス等あったらすいません。
今回Androidは省いています。ARCore触り出したらまた作るかもしれません...

ぜひARKitやVuforia等を用いたアプリと組み合わせてみてください。

UnityProject(ソースコード)はこちら
https://github.com/Ryopon/iOSScreenCapture

追記(18/02/01)
手動で設定していた部分の自動化等を行いました。
それに伴い記事を少し修正しています。

環境

Unity2017.1.f1
iOS11

カメラと写真の利用許可を求める

ネイティブ側のコードはこちらを見てください。
Unity側(C#)では下の様なコードで呼び出しを行います。

UnityiOS.cs
public class UnityiOS {
...
    public static void RequestPermissions() {
        AVAuthorizationStatus avstatus = HasCameraPermission();
        PHAuthorizationStatus phstatus = HasCameraRollPermission();

        //アクセス許可のリクエストを出していない場合はリクエストを送る
        if (avstatus == AVAuthorizationStatus.NotDetermined) {
            _RequestCameraPermission();
        }

        if(phstatus == PHAuthorizationStatus.NotDetermined) {
            _RequestCameraRollPermission();
        }
    }
...
}

RequestPermissions関数を呼び出すことでネイティブコードが実行され、カメラと写真へのアクセス許可を求めるポップアップが表示されます。
また、AuthorizationがNotDeterminedの場合にリクエストを求める事で初回起動時のみリクエストが表示されます。

利用許可がもらえなかった場合の処理

ユーザーによってアクセスを拒否された場合はこんな感じの画面を出したいですよね。
スクリーンショット 2017-09-26 19.11.32.png

アクセス許可を持っているかどうかはHasCamera(Roll)Permissionで調べる事が出来ますので

Example
if(UnityiOS.HasCameraPermission()) {
  //処理
}

こんな感じで調べて画面の表示を行ってください。

ついでに「はい」押したら設定画面に飛んで欲しいですよね!

UnityiOS.cs
...
    public static void GoToSettings() {
#if !UNITY_EDITOR
        _GoToSettings();
#endif
    }

    public void GoToSettings_forUGUI() {
#if !UNITY_EDITOR
        _GoToSettings();
#endif
    }
...

こんな関数を用意していますので
スクリーンショット 2017-09-28 11.02.14.png

uGUIボタンのOnClickに入れておくと、ボタン押下時にアプリの設定が開くようになります。
ボタンから静的関数を呼び出すことは出来ない(GoToSettings関数等)ので注意してください(missingが表示されます)。

写真を撮ってカメラロールに保存する

撮影を実行するにはUnityiOSScreenCapture.Executeを実行します。こちらもGoToSettings_forUGUIと同様にuGUIボタンのOnClickに入れておくと、撮影ボタンを作れます。

UnityiOSScreenCapture.cs
public class UnityiOSScreenCapture : MonoBehaviour {

    public UnityEvent OnCompleteCapture;
    public UnityEvent OnFailCapture;

    public void Execute() {
#if !UNITY_EDITOR
        PHAuthorizationStatus phstatus = (PHAuthorizationStatus)Enum.ToObject(
            typeof(PHAuthorizationStatus), UnityiOS.HasCameraRollPermission());
        UnityiOS.PlaySystemShutterSound();
        if(phstatus == PHAuthorizationStatus.Authorized) {
            Handheld.SetActivityIndicatorStyle(UnityEngine.iOS.ActivityIndicatorStyle.Gray);
            Handheld.StartActivityIndicator();
        } else {
            OnFailCapture.Invoke();
        }
#endif
    }
...
}

撮影成功時にOnCompleteCapture、写真へのアクセスが非許可設定になってる場合にOnFailCaptureが実行されるようになっていますので、こちらもインスペクタ上で設定してあげてください。

また、ネイティブからのコールバックを受ける関係でヒエラルキーに指定した名前のゲームオブジェクトを作成する必要があります。(この辺りもっと上手く作れるはずなのでプルリクお待ちしてます!!)
スクリーンショット 2017-09-28 12.04.26.png

今回は"UnityiOSScreenCapture"というゲームオブジェクトにコールバックを返すので、このオブジェクトにUnityiOSScreenCaptureをAddComponentして使います。
コールバックを返す対象はCaptureCallback.mmに記述していますので、変更したい場合はこれをイジって下さい。

    UnitySendMessage("UnityiOSScreenCapture", "DidImageWriteToAlbum", "");

XCodeの設定(不要になりました)

XCodeで追加していた設定をビルド時に自動で追加してくれるエディタ拡張を追加しました。
IOSSCBuildPostProcessor.cs
カメラロールへのアクセスを求める際に表示する説明文は

IOSSCBuildPostProcessor.cs
public class IOSSCBuildPostProcessor
{
...
    public static void OnPostProcessBuildIOS(string pathToBuiltProject)
    {
#if UNITY_IOS
...
        const string description = "Camera roll access permission is necessary for saving an image.";
...
#endif //UNITY_IOS
    }
}

OnPostProcessBuildIOS関数内のdescriptionの中身を書き換えて下さい。

Unityだけでは行えない設定があるので、XCode側で少し設定を加える必要があります。
まずはGeneral > Linked Frameworks And LibrariesPhotos.frameworkを追加します。

スクリーンショット 2017-09-28 12.39.46.png

次にInfo > Custom iOS Target Propertiesで以下の2つを追加します。

  • Privacy - Photo Library Usage Description
  • Privacy - Camera Usage Description

カメラについてはUnityでも指定出来ますが、写真についてはXCode側で設定する必要があるので今回はまとめてこちらで設定しています。
スクリーンショット 2017-09-28 15.30.50.png

以上でカメラロールにスクショを保存するまでのアレコレは終了です。