Update models
This commit is contained in:
142
CapCollector/Capture/CameraController.swift
Normal file
142
CapCollector/Capture/CameraController.swift
Normal file
@ -0,0 +1,142 @@
|
||||
//
|
||||
// CameraController.swift
|
||||
// CapFinder
|
||||
//
|
||||
// Created by User on 22.02.18.
|
||||
// Copyright © 2018 User. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol CameraControllerDelegate {
|
||||
|
||||
func didCapture(image: UIImage)
|
||||
|
||||
func didCancel()
|
||||
}
|
||||
|
||||
class CameraController: UIViewController {
|
||||
|
||||
private let imageSize = 299 // New for XCode models, 227 for turicreate
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet weak var imageButton: RoundedButton!
|
||||
|
||||
@IBOutlet weak var cropView: CropView!
|
||||
|
||||
@IBOutlet weak var cancelButton: RoundedButton!
|
||||
|
||||
@IBOutlet weak var cameraView: CameraView! {
|
||||
didSet {
|
||||
cameraView.configure()
|
||||
}
|
||||
}
|
||||
|
||||
var delegate: CameraControllerDelegate?
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func backButtonPressed() {
|
||||
delegate?.didCancel()
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func imageButtonPressed() {
|
||||
imageButton.isEnabled = false
|
||||
event("Taking image")
|
||||
cameraView.capture()
|
||||
}
|
||||
|
||||
// MARK: Life cycle
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
cameraView.delegate = self
|
||||
imageButton.imageView?.image = UIImage.templateImage(named: "camera")
|
||||
setTintColor()
|
||||
|
||||
cameraView.launch { success, error in
|
||||
guard let err = error else {
|
||||
return
|
||||
}
|
||||
switch err {
|
||||
case "No camera access": self.showNoCameraAccessAlert()
|
||||
case "Camera error": self.showAlert("Unable to capture media")
|
||||
default: self.showAlert("Error in camera setup")
|
||||
}
|
||||
}
|
||||
self.imageButton.isEnabled = true
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
cameraView.complete()
|
||||
}
|
||||
|
||||
private func setTintColor() {
|
||||
let tint = AppDelegate.tintColor
|
||||
cropView.lineColor = tint
|
||||
imageButton.borderColor = tint
|
||||
imageButton.imageView?.tintColor = tint
|
||||
cancelButton.borderColor = tint
|
||||
cancelButton.set(template: "cancel", with: tint)
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
private func showNoCameraAccessAlert() {
|
||||
let alert = UIAlertController(title: "Unable to access the Camera",
|
||||
message: "To enable access, go to Settings > Privacy > Camera and turn on Camera access for this app.",
|
||||
preferredStyle: .alert,
|
||||
blurStyle: .dark)
|
||||
|
||||
let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
|
||||
alert.addAction(okAction)
|
||||
|
||||
let settingsAction = UIAlertAction(title: "Settings", style: .default, handler: { _ in
|
||||
// Take the user to Settings app to possibly change permission.
|
||||
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||
if UIApplication.shared.canOpenURL(settingsUrl) {
|
||||
UIApplication.shared.open(settingsUrl, completionHandler: nil)
|
||||
}
|
||||
})
|
||||
alert.addAction(settingsAction)
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension CameraController: PhotoCaptureHandlerDelegate {
|
||||
|
||||
func didCapture(_ image: UIImage?) {
|
||||
event("Image captured")
|
||||
let factor = CGFloat(cropView.relativeSize)
|
||||
self.dismiss(animated: true)
|
||||
let size = CGSize(width: imageSize, height: imageSize)
|
||||
guard let img = image else {
|
||||
self.error("No image captured")
|
||||
return
|
||||
}
|
||||
|
||||
guard delegate != nil else {
|
||||
self.error("No delegate")
|
||||
return
|
||||
}
|
||||
|
||||
guard let masked = img.crop(factor: factor).circleMasked else {
|
||||
self.error("Could not mask image")
|
||||
return
|
||||
}
|
||||
|
||||
// Only use 227 x 227 image
|
||||
let scaled = masked.resize(to: size)
|
||||
|
||||
delegate!.didCapture(image: scaled)
|
||||
}
|
||||
}
|
||||
|
||||
extension CameraController: Logger {
|
||||
|
||||
static var logToken = "[Camera]"
|
||||
}
|
197
CapCollector/Capture/CameraView.swift
Normal file
197
CapCollector/Capture/CameraView.swift
Normal file
@ -0,0 +1,197 @@
|
||||
//
|
||||
// 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 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.
|
||||
do {
|
||||
guard let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
|
||||
print("No camera on device")
|
||||
setupResult = .configurationFailed
|
||||
session.commitConfiguration()
|
||||
return
|
||||
}
|
||||
|
||||
let videoDeviceInput = try AVCaptureDeviceInput(device: backCameraDevice)
|
||||
|
||||
if session.canAddInput(videoDeviceInput) {
|
||||
session.addInput(videoDeviceInput)
|
||||
self.videoDeviceInput = videoDeviceInput
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.videoPreviewLayer.connection?.videoOrientation = .portrait
|
||||
}
|
||||
} else {
|
||||
print("Could not add video device input to the session")
|
||||
setupResult = .configurationFailed
|
||||
session.commitConfiguration()
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
print("Could not create video device input: \(error)")
|
||||
setupResult = .configurationFailed
|
||||
session.commitConfiguration()
|
||||
return
|
||||
}
|
||||
|
||||
// Add photo output.
|
||||
if session.canAddOutput(photoOutput) {
|
||||
session.addOutput(photoOutput)
|
||||
|
||||
photoOutput.isHighResolutionCaptureEnabled = true
|
||||
photoOutput.isDepthDataDeliveryEnabled = false
|
||||
photoOutput.isDualCameraDualPhotoDeliveryEnabled = false
|
||||
photoOutput.isLivePhotoCaptureEnabled = false
|
||||
} else {
|
||||
print("Could not add photo output to the session")
|
||||
setupResult = .configurationFailed
|
||||
session.commitConfiguration()
|
||||
return
|
||||
}
|
||||
|
||||
session.commitConfiguration()
|
||||
}
|
||||
}
|
||||
|
51
CapCollector/Capture/PhotoCaptureHandler.swift
Normal file
51
CapCollector/Capture/PhotoCaptureHandler.swift
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
See LICENSE.txt for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Photo capture delegate.
|
||||
*/
|
||||
|
||||
import AVFoundation
|
||||
import Photos
|
||||
|
||||
protocol PhotoCaptureHandlerDelegate {
|
||||
|
||||
func didCapture(_ image: UIImage?)
|
||||
}
|
||||
|
||||
class PhotoCaptureHandler: NSObject {
|
||||
|
||||
var delegate: PhotoCaptureHandlerDelegate?
|
||||
|
||||
var photoSettings: AVCapturePhotoSettings {
|
||||
let photoSettings = AVCapturePhotoSettings()
|
||||
photoSettings.flashMode = .off
|
||||
return photoSettings
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotoCaptureHandler: AVCapturePhotoCaptureDelegate {
|
||||
/*
|
||||
This extension includes all the delegate callbacks for AVCapturePhotoCaptureDelegate protocol
|
||||
*/
|
||||
|
||||
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
|
||||
|
||||
guard error == nil else {
|
||||
print("PhotoCaptureHandler: \(error!)")
|
||||
delegate?.didCapture(nil)
|
||||
return
|
||||
}
|
||||
|
||||
guard let cgImage = photo.cgImageRepresentation()?.takeUnretainedValue() else {
|
||||
print("PhotoCaptureHandler: No image captured")
|
||||
delegate?.didCapture(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let image = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.didCapture(image)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user