228 lines
6.6 KiB
Swift
228 lines
6.6 KiB
Swift
//
|
|
// 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"
|
|
}
|