iOS10でUIImagePickerController.cameraOverlayViewに設定したビューのユーザインタラクションが効かないのでサブクラスを作って回避


iOS9の時に作ったカメラアプリで`UIImagePickerController.cameraOverlayView`にオーバレイビューを設定して，そのオーバレイビューにジェスチャを付けていたのですが，iOS10にアップデートしたらジェスチャに反応しなくなってしまいました．

私の実装の問題かもしれませんが，取り急ぎ回避策を用意して対処したので残しておきます．

## 環境
* Mac mini (Mid 2011) CPU2.5GHz, Memory16GB
* XCode 8.0 (8A218a)
    * Deployment Target: 9.0
    * Devices: iPhone
    * Base SDK: iOS 10.0
    * 本文中のサンプルプログラムは新規プロジェクトとして作成した"Single View Application"をベースにしています
* 確認した実機
    * iPhone SE (iOS 10.0.2)
    * iPad 3rd (iOS  9.3.5) ※iOS9のiPhoneが無かったので

## だめなパターン（iOS9では動く）
起動したらタップジェスチャに反応するオーバレイビューを設定した`UIImagePickerController`を表示するプログラムです．
[iOS10からInfo.plistにプライバシーに関する説明を入れないといけない](http://qiita.com/Takumi_Mori/items/f53c6eec1676d3df59dc)ようなので，`Info.plist`も編集しています．

``` ViewController.swift
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    var imagePicker: UIImagePickerController?
    
    override func awakeFromNib() {
        // 左半分を覆うオーバレイビュー
        let halfLeftRect = CGRect(x: 0, y: 0, width: self.view.bounds.width / 2.0, height: self.view.bounds.height)
        let overlayView = UIView(frame: halfLeftRect)
        overlayView.backgroundColor = UIColor.blue
        overlayView.isUserInteractionEnabled = true // デフォルトtrueだが念のため
        
        // タップジェスチャをセット
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapped(_:)))
        overlayView.addGestureRecognizer(tapGesture)
        
        // カメラ使えるかチェック
        if !UIImagePickerController.isSourceTypeAvailable(.camera) {
            print("camera isn't available")
        }
        else {
            // オーバレイをセットしたイメージピッカー
            imagePicker = UIImagePickerController()
            imagePicker!.sourceType = .camera
            imagePicker!.cameraOverlayView = overlayView
            imagePicker!.showsCameraControls = false
        }
    }
    
    func tapped(_ sender: UIGestureRecognizer) {
        print("TAPPED.")
    }
    
    override func viewDidAppear(_ animated: Bool) {
        // イメージピッカー表示
        if let imagePicker = self.imagePicker {
            present(imagePicker, animated: true, completion: nil)
        }
    }
}
```

``` Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NSCameraUsageDescription</key>
	<string>許可してね</string>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>
```

iPhone SE (iOS 10.0.2)とiPad 3rd (iOS 9.3.5)でそれぞれで起動すると．


 | アプリ画面 | Xcodeコンソール
:-- | :-- | :--
iPhone SE (iOS 10.0.2) | ![iphone_tap_before.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/b5436068-a869-cb65-0a28-be2b277079d2.jpeg "iphone_tap_before.jpg") | ![iphone_before.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/63adb2c8-4325-28ea-a5e9-1a3b25f347c2.jpeg "iphone_before.jpg")
iPad 3rd (iOS 9.3.5) | ![ipad_tap_before.jpg.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/c3c175d6-f5ea-4cbb-abd2-bd5787e294d0.jpeg "ipad_tap_before.jpg.jpg") | ![ipad_before.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/6b955086-3312-6196-cdcb-9f7e2d74d3b2.jpeg "ipad_before.jpg")

iOS10の方だけジェスチャが効きませんでした！
iOS10の方はタップするとカメラのピントを合わせるような動きをするので，`isUserInteractionEnalbed = true`としているはずのオーバレイビューがユーザインタラクションを取れていないように見えます．

## 回避策（iOS9/10両方で動く）
`UIImagePickerController`のサブクラスを作り，`camearaOverlayView`にセットされるビューを独自にサブビューとして表示するようにしました．（`UIImagePickerController.camearaOverlayView`に期待されるすべての動作に互換性があるかは確認していません．）
`ViewController.swift`は`UIImagePickerController`を`MyImagePickerController`に変更し，オーバレイビューの背景色を黄色に変更しています．

``` ViewController.swift
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    var imagePicker: MyImagePickerController?
    
    override func awakeFromNib() {
        // 左半分を覆うオーバレイビュー
        let halfLeftRect = CGRect(x: 0, y: 0, width: self.view.bounds.width / 2.0, height: self.view.bounds.height)
        let overlayView = UIView(frame: halfLeftRect)
        overlayView.backgroundColor = UIColor.yellow
        overlayView.isUserInteractionEnabled = true // デフォルトtrueだが念のため
        
        // タップジェスチャをセット
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapped(_:)))
        overlayView.addGestureRecognizer(tapGesture)
        
        // カメラ使えるかチェック
        if !UIImagePickerController.isSourceTypeAvailable(.camera) {
            print("camera isn't available")
        }
        else {
            // オーバレイをセットしたイメージピッカー
            imagePicker = MyImagePickerController()
            imagePicker!.sourceType = .camera
            imagePicker!.cameraOverlayView = overlayView
            imagePicker!.showsCameraControls = false
        }
    }
    
    func tapped(_ sender: UIGestureRecognizer) {
        print("TAPPED.")
    }
    
    override func viewDidAppear(_ animated: Bool) {
        // イメージピッカー表示
        if let imagePicker = self.imagePicker {
            present(imagePicker, animated: true, completion: nil)
        }
    }
}
```

``` MyImagePickerController.swift
import UIKit

class MyImagePickerController: UIImagePickerController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // オリジナルのオーバレイビュー
    private var myCameraOverlayView: UIView?
    
    // スーパークラスのプロパティを横取りしてオリジナルオーバレイビューを表示用に利用する
    override var cameraOverlayView: UIView? {
        get {
            return myCameraOverlayView
        }
        set {
            myCameraOverlayView?.removeFromSuperview()
            if let newValue = newValue {
                myCameraOverlayView = newValue
                super.view.addSubview(newValue)
            }
        }
    }
}
```

``` Info.plist
（「だめなパターン」と同じ）
```

iPhone SE (iOS 10.0.2)とiPad 3rd (iOS 9.3.5)でそれぞれで起動すると．


 | アプリ画面 | Xcodeコンソール
:-- | :-- | :--
iPhone SE (iOS 10.0.2) | ![iphone_tap_after.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/1a38dc13-9cda-b913-93af-07ff50a973c5.jpeg "iphone_tap_after.jpg") | ![iphone_after.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/620eabe0-4c5e-e34b-1b73-7c08530869ab.jpeg "iphone_after.jpg")
iPad 3rd (iOS 9.3.5) | ![ipad_tap_after.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/1d08818b-fa1d-e3e3-9566-7893ad5e9f4b.jpeg "ipad_tap_after.jpg") | ![ipad_after.jpg](https://qiita-image-store.s3.amazonaws.com/0/35592/72c618b9-4252-7efd-26ff-2f726001e721.jpeg "ipad_after.jpg")

両方ともジェスチャが効くようになりました！