Skip to content

Instantly share code, notes, and snippets.

@vendruscolo
Created February 21, 2017 09:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vendruscolo/31fa0914d8c485fee68940960f1e9f37 to your computer and use it in GitHub Desktop.
Save vendruscolo/31fa0914d8c485fee68940960f1e9f37 to your computer and use it in GitHub Desktop.
//
// CameraView.swift
// ALCameraViewController
//
// Created by Alex Littlejohn on 2015/06/17.
// Copyright (c) 2015 zero. All rights reserved.
//
import UIKit
import AVFoundation
import Dispatch
// Adapted from ALCameraViewController
// https://github.com/AlexLittlejohn/ALCameraViewController/blob/fce8499e4ccd52a2389c4b215d718a544b7aba51/ALCameraViewController/Views/CameraView.swift
/// CameraView is an object that manages the preview layer of the camera.
///
/// Construct one and retrieve the `previewLayer`. Also call the `startSession`
/// and `stopSession` when appropriate.
class CameraPreviewLayer: NSObject {
// MARK: Init
init(targetSize: CGSize) {
super.init()
// AVFoundation boilerplate...
session = AVCaptureSession()
session.sessionPreset = AVCaptureSessionPresetPhoto
device = cameraWithPosition(.back)
if let device = device, device.hasFlash {
do {
try device.lockForConfiguration()
device.flashMode = .auto
device.unlockForConfiguration()
} catch _ {}
}
let outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
do {
input = try AVCaptureDeviceInput(device: device)
} catch let error as NSError {
input = nil
fatalError("Error: \(error.localizedDescription)")
}
if session.canAddInput(input) {
session.addInput(input)
}
imageOutput = AVCaptureStillImageOutput()
imageOutput.outputSettings = outputSettings
session.addOutput(imageOutput)
// Create the layer
previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.frame = CGRect(origin: CGPoint.zero, size: targetSize)
// Handle rotation
NotificationCenter.default.addObserver(
self,
selector: #selector(CameraPreviewLayer.deviceOrientationDidChange),
name: NSNotification.Name.UIDeviceOrientationDidChange,
object: nil
)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
stopSession()
// Zeroing-weak observers? Who knows if that works...
NotificationCenter.default.removeObserver(self)
}
// MARK: Public properties
/// The AVCaptureVideoPreviewLayer that shows the camera live feed.
var previewLayer: AVCaptureVideoPreviewLayer!
// MARK: Public methods
/// Starts the AVCaptureSession. If the session is stopped (or not yet
/// started) the view will be black.
func startSession() {
cameraQueue.sync {
self.session.startRunning()
}
// Detect orientation changes
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
}
/// Stops the AVCaptureSession. This will also be called on deinit
/// automatically.
func stopSession() {
// We're no longer insterested in orientation changes
UIDevice.current.endGeneratingDeviceOrientationNotifications()
cameraQueue.sync {
self.session.stopRunning()
}
}
// MARK: Private methods
private func cameraWithPosition(_ position: AVCaptureDevicePosition) -> AVCaptureDevice? {
guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else {
return nil
}
return devices.filter { $0.position == position }.first
}
private func rotatePreview() {
switch UIDevice.current.orientation {
case .portrait:
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
case .portraitUpsideDown:
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
case .landscapeLeft:
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.landscapeRight
case .landscapeRight:
previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
default: break
}
}
@objc private func deviceOrientationDidChange() {
rotatePreview()
}
// MARK: Private properties
var session: AVCaptureSession!
var input: AVCaptureDeviceInput!
var device: AVCaptureDevice!
var imageOutput: AVCaptureStillImageOutput!
let cameraQueue = DispatchQueue(label: "com.zero.ALCameraViewController.Queue", attributes: [])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment