// // CameraView.swift // CapFinder // // Created by User on 07.02.18. // Copyright © 2018 User. All rights reserved. // import UIKit import AVFoundation class CameraView: UIView { // MARK: UIView overrides /** Override for AVCapture */ override class var layerClass: AnyClass { return AVCaptureVideoPreviewLayer.self } // MARK: Enums private enum SessionSetupResult { case success case notAuthorized case configurationFailed } // MARK: Variables var delegate: PhotoCaptureHandlerDelegate? { get { return photoCaptureProcessor.delegate } set { photoCaptureProcessor.delegate = newValue } } private let session = AVCaptureSession() private var isSessionRunning = false /// Communicate with the session and other session objects on this queue. private let sessionQueue = DispatchQueue(label: "session queue") private var setupResult: SessionSetupResult = .success var videoDeviceInput: AVCaptureDeviceInput! private let photoOutput = AVCapturePhotoOutput() private var cameraDevice: AVCaptureDevice? private let photoCaptureProcessor = PhotoCaptureHandler() var videoPreviewLayer: AVCaptureVideoPreviewLayer { return layer as! AVCaptureVideoPreviewLayer } // MARK: Life cycle func configure() { videoPreviewLayer.session = session checkPermission() // Setup the capture session. sessionQueue.async { self.configureSession() } } func launch(completionHandler: @escaping (Bool, String?) -> ()) { sessionQueue.async { switch self.setupResult { case .success: // Only setup observers and start the session running if setup succeeded. self.session.startRunning() self.isSessionRunning = self.session.isRunning case .notAuthorized: DispatchQueue.main.async { completionHandler(false, "No camera access") } case .configurationFailed: DispatchQueue.main.async { completionHandler(false, "Camera error") } } } } func complete() { sessionQueue.async { if self.setupResult == .success { self.session.stopRunning() self.isSessionRunning = self.session.isRunning } } } // MARK: Photo Capture func capture() { sessionQueue.async { self.photoOutput.capturePhoto( with: self.photoCaptureProcessor.photoSettings, delegate: self.photoCaptureProcessor) } } // MARK: Camera permissions private func checkPermission() { switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: break case .notDetermined: sessionQueue.suspend() AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in if !granted { self.setupResult = .notAuthorized } self.sessionQueue.resume() }) default: // The user has previously denied access. setupResult = .notAuthorized } } // Call this on the session queue. private func configureSession() { if setupResult != .success { return } session.beginConfiguration() /* We do not create an AVCaptureMovieFileOutput when setting up the session because the AVCaptureMovieFileOutput does not support movie recording with AVCaptureSession.Preset.Photo. */ session.sessionPreset = .photo // Add video input. guard let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { error("No camera on device") setupResult = .configurationFailed session.commitConfiguration() return } let videoDeviceInput: AVCaptureDeviceInput do { videoDeviceInput = try AVCaptureDeviceInput(device: backCameraDevice) } catch { self.error("Could not create video device input: \(error)") setupResult = .configurationFailed session.commitConfiguration() return } guard session.canAddInput(videoDeviceInput) else { error("Could not add video device input to the session") setupResult = .configurationFailed session.commitConfiguration() return } session.addInput(videoDeviceInput) self.videoDeviceInput = videoDeviceInput self.cameraDevice = backCameraDevice DispatchQueue.main.async { self.videoPreviewLayer.connection?.videoOrientation = .portrait } // Add photo output. guard session.canAddOutput(photoOutput) else { error("Could not add photo output to the session") setupResult = .configurationFailed session.commitConfiguration() return } session.addOutput(photoOutput) photoOutput.isHighResolutionCaptureEnabled = true photoOutput.isDepthDataDeliveryEnabled = false photoOutput.isDualCameraDualPhotoDeliveryEnabled = false photoOutput.isLivePhotoCaptureEnabled = false session.commitConfiguration() } func didReceiveTouch(_ touch: UITouch) { let screenSize = bounds.size let location = touch.location(in: self) let focusPoint = CGPoint(x: location.y / screenSize.height, y: 1.0 - location.x / screenSize.width) event("Focusing on point (\(focusPoint.x),\(focusPoint.y))") if let device = cameraDevice { do { try device.lockForConfiguration() if device.isFocusPointOfInterestSupported { device.focusPointOfInterest = focusPoint device.focusMode = .autoFocus } // if device.isExposurePointOfInterestSupported { // device.exposurePointOfInterest = focusPoint // device.exposureMode = .autoExpose // } device.unlockForConfiguration() } catch { self.error("Could not lock device for configuration: \(error)") } } } } extension CameraView: Logger { static let logToken = "CameraView" }