ottijp blog

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

  • 2026-09-27

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も編集しています.

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 iphone before
iPad 3rd (iOS 9.3.5) ipad tap before ipad before

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

回避策(iOS9/10両方で動く)

UIImagePickerControllerのサブクラスを作り,camearaOverlayViewにセットされるビューを独自にサブビューとして表示するようにしました.(UIImagePickerController.camearaOverlayViewに期待されるすべての動作に互換性があるかは確認していません.) ViewController.swiftUIImagePickerControllerMyImagePickerControllerに変更し,オーバレイビューの背景色を黄色に変更しています.

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 iphone after
iPad 3rd (iOS 9.3.5) ipad tap after ipad after

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


ottijp
都内でアプリケーションエンジニアをしています
© 2026, ottijp