iOS8以降でカメラを実装する際に注意したいPermissonについて


はじめに

今回、新規アプリをリリースするにあたり実機テストを行っていたところ
特定のiPhoneでカメラが正常に機能しないという不具合を発見。
すぐに解消できるバグだろうと思いながら原因を探っていたものの、
意外にハマってしまったので少し紹介したいと思います。

プロジェクト環境と対応機種

  • OS X El Capitan
  • Xcode 7.3
  • Target iOS8 or later
  • iPhone 5S, 6, 6S, 6 Plus, 6S Plus

どんな症状か?

撮影画面は表示できる。
が、これ以上何もできない状態でした。

問題の特徴を挙げると次の通りでした。

  • 画面が真っ暗
  • 「キャンセル」以外のボタン・タップに反応しない
  • 撮影できない

ボタンを押して撮影画面が立ち上がったものの、
機能が制限された状態という感じです。

カメラ機能を実装する

一般的なカメラ機能の実装といえば、おそらく二通りほど。
今回のプロジェクトではUIImagePickerViewControllerで実装しました。

ViewController.m

@interface AddViewController () <UIImagePickerControllerDelegate,UINavigationControllerDelegate>
- (void)openCamara;
@end

@implementation ViewController
...
#pragma mark - UIImagePickerControllerDelegate

- (void)openCamara {

    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;

    if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
        UIImagePickerController *imagePicker = [UIImagePickerController new];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentViewController:imagePicker animated:YES completion:nil];
    } else {
        NSLog(@"現在カメラが利用できません。");
    }

}

...

@end

メソッドの最初で宣言しているUIImagePickerControllerSourceTypeは、
UIImagePickerControllerDelegateで定義されたNS_ENUMです。
用途(カメラ/写真/フォトアルバム)に応じて、いづれかを宣言します。

  1. UIImagePickerControllerSourceTypePhotoLibrary
  2. UIImagePickerControllerSourceTypeCamera
  3. UIImagePickerControllerSourceTypeSavedPhotosAlbum

カメラを利用する場合にはUIImagePickerControllerSourceTypeCameraを選びます。
カメラが利用できるかどうかはisSourceTypeAvailableで確認できます。
もしカメラの機能が制限されていたり、デバイスにカメラが存在しない場合にはNOが返されます。
なので、実際にSimulator上でRunする場合すればNOが確認できます。

しかし、今回のケースではYESで通っていました。
どうやらデバイスはカメラを認識しているようですが、
何か別の原因によって撮影機能が制限されているようです。

誰がパーミッションを決めるか?

ここが抜けていたことが一番の問題点でした。

iOS7からiOS8になったとき、デバイス使用のパーミッションの扱い方が変更になりました。
その一つが、アプリごとにプライバシー設定が選べるようになったことです。
iOS7では、カメラの使用に関しては全く制限がありませんでした。
いわばiPhone自身が、ユーザーから承諾を得ることなく、自由にカメラの使用ができたのです。
しかし、iOS8以降、ユーザーがその使用の決定権をもつことで、
プライバシー保護の強化が図られているようです。

プライバシーを設定する

カメラのプライバシーは、AVFoundationAVAuthorizationStatusから確認することができます。
早速、プライバシー設定を変更するための処理を加えたところ、カメラが正常に動作するようになりました。
特にアプリのインストール直後は、AVAuthorizationStatusAVAuthorizationStatusNotDetermined
となっているため、プライバシー設定の確認後、ダイアログを表示して、変更の処理を行うような処理を加えるといいかもしれません。

どうやら、これを怠ったためにカメラが使用できなかったようです。

#import <AVFoundation/AVFoundation.h>

    ...

    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

    if (status == AVAuthorizationStatusAuthorized) { // プライバシー設定でカメラ使用が許可されている

    } else if (status == AVAuthorizationStatusDenied) { //  不許可になっている

    } else if (status == AVAuthorizationStatusRestricted) { // 制限されている

    } else if (status == AVAuthorizationStatusNotDetermined) { // アプリで初めてカメラ機能を使用する場合

       [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {          

          if (granted) { // 使用が許可された場合

          } else {       // 使用が不許可になった場合

          }

       }];

    }

(追記)
iOS7ではmediaTypeにAVMediaTypeVideoを指定した場合、
常にAVAuthorizationStatusAuthorizedを返していましたが、
iOS8からカメラのプライバシー設定の判定用メソッドとして機能するようになったようです。

参考資料

AVFoundation Framework (iOS Developer Library
Presenting camera permission dialog in iOS 8
How to Manage App Permissions on Your iPhone or iPad
iOSでカメラと写真の利用許可の確認方法
iOSでUIImagePickerControllerの権限チェックと設定画面への誘導を行うライブラリ