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にプライバシーに関する説明を入れないといけないようなので,Info.plistも編集しています.
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)
}
}
}<?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) |
|
|
| iPad 3rd (iOS 9.3.5) |
|
|
iOS10の方だけジェスチャが効きませんでした!
iOS10の方はタップするとカメラのピントを合わせるような動きをするので,isUserInteractionEnalbed = trueとしているはずのオーバレイビューがユーザインタラクションを取れていないように見えます.
回避策(iOS9/10両方で動く)
UIImagePickerControllerのサブクラスを作り,camearaOverlayViewにセットされるビューを独自にサブビューとして表示するようにしました.(UIImagePickerController.camearaOverlayViewに期待されるすべての動作に互換性があるかは確認していません.)
ViewController.swiftはUIImagePickerControllerをMyImagePickerControllerに変更し,オーバレイビューの背景色を黄色に変更しています.
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)
}
}
}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)
}
}
}
}(「だめなパターン」と同じ)iPhone SE (iOS 10.0.2)とiPad 3rd (iOS 9.3.5)でそれぞれで起動すると.
| アプリ画面 | Xcodeコンソール | |
|---|---|---|
| iPhone SE (iOS 10.0.2) |
|
|
| iPad 3rd (iOS 9.3.5) |
|
|
両方ともジェスチャが効くようになりました!