Skip to content

Instantly share code, notes, and snippets.

@wuaschtikus
Created March 22, 2019 06:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wuaschtikus/f0134028f50d881e0784c00034b60923 to your computer and use it in GitHub Desktop.
Save wuaschtikus/f0134028f50d881e0784c00034b60923 to your computer and use it in GitHub Desktop.
import UIKit
import AVFoundation
class Camera: NSObject {
var captureSession: AVCaptureSession?
var currentCameraPosition: CameraPosition?
var frontCamera: AVCaptureDevice?
var frontCameraInput: AVCaptureDeviceInput?
var photoOutput: AVCapturePhotoOutput?
var rearCamera: AVCaptureDevice?
var rearCameraInput: AVCaptureDeviceInput?
var previewLayer: AVCaptureVideoPreviewLayer?
var flashMode = AVCaptureDevice.FlashMode.off
var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
}
extension Camera {
func prepare(completionHandler: @escaping (Error?) -> Void) {
func createCaptureSession() {
self.captureSession = AVCaptureSession()
}
func configureCaptureDevices() throws {
let session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .unspecified)
let cameras = session.devices.compactMap { $0 }
guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
for camera in cameras {
if camera.position == .front {
self.frontCamera = camera
}
if camera.position == .back {
self.rearCamera = camera
try camera.lockForConfiguration()
camera.focusMode = .continuousAutoFocus
camera.unlockForConfiguration()
}
}
}
func configureDeviceInputs() throws {
guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
if let rearCamera = self.rearCamera {
self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }
self.currentCameraPosition = .rear
}
else if let frontCamera = self.frontCamera {
self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) }
else { throw CameraControllerError.inputsAreInvalid }
self.currentCameraPosition = .front
}
else { throw CameraControllerError.noCamerasAvailable }
}
func configurePhotoOutput() throws {
guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
self.photoOutput = AVCapturePhotoOutput()
self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecJPEG])], completionHandler: nil)
if captureSession.canAddOutput(self.photoOutput!) { captureSession.addOutput(self.photoOutput!) }
captureSession.startRunning()
}
DispatchQueue(label: "prepare").async {
do {
createCaptureSession()
try configureCaptureDevices()
try configureDeviceInputs()
try configurePhotoOutput()
}
catch {
DispatchQueue.main.async {
completionHandler(error)
}
return
}
DispatchQueue.main.async {
completionHandler(nil)
}
}
}
func displayPreview(on view: UIView) throws {
guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = .portrait
view.layer.insertSublayer(self.previewLayer!, at: 0)
self.previewLayer?.frame = view.frame
}
func switchCameras() throws {
guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
captureSession.beginConfiguration()
func switchToFrontCamera() throws {
guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput),
let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
captureSession.removeInput(rearCameraInput)
if captureSession.canAddInput(self.frontCameraInput!) {
captureSession.addInput(self.frontCameraInput!)
self.currentCameraPosition = .front
}
else {
throw CameraControllerError.invalidOperation
}
}
func switchToRearCamera() throws {
guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
captureSession.removeInput(frontCameraInput)
if captureSession.canAddInput(self.rearCameraInput!) {
captureSession.addInput(self.rearCameraInput!)
self.currentCameraPosition = .rear
}
else { throw CameraControllerError.invalidOperation }
}
switch currentCameraPosition {
case .front:
try switchToRearCamera()
case .rear:
try switchToFrontCamera()
}
captureSession.commitConfiguration()
}
func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, CameraControllerError.captureSessionIsMissing); return }
let settings = AVCapturePhotoSettings()
settings.flashMode = self.flashMode
self.photoOutput?.capturePhoto(with: settings, delegate: self)
self.photoCaptureCompletionBlock = completion
}
}
extension Camera: AVCapturePhotoCaptureDelegate {
public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
if let error = error { self.photoCaptureCompletionBlock?(nil, error) }
else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
let image = UIImage(data: data) {
self.photoCaptureCompletionBlock?(image, nil)
}
else {
self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
}
}
}
extension Camera {
enum CameraControllerError: Swift.Error {
case captureSessionAlreadyRunning
case captureSessionIsMissing
case inputsAreInvalid
case invalidOperation
case noCamerasAvailable
case unknown
}
public enum CameraPosition {
case front
case rear
}
}
// Camera Controller
import UIKit
import Photos
class CameraController: UIViewController {
@IBOutlet fileprivate var captureButton: UIButton!
///Displays a preview of the video output generated by the device's cameras.
@IBOutlet fileprivate var capturePreviewView: UIView!
///Allows the user to put the camera in photo mode.
@IBOutlet fileprivate var photoModeButton: UIButton!
@IBOutlet fileprivate var toggleCameraButton: UIButton!
@IBOutlet fileprivate var toggleFlashButton: UIButton!
///Allows the user to put the camera in video mode.
@IBOutlet fileprivate var videoModeButton: UIButton!
let camera = Camera()
override var prefersStatusBarHidden: Bool { return true }
}
extension CameraController {
override func viewDidLoad() {
func configureCameraController() {
camera.prepare {(error) in
if let error = error {
print(error)
}
try? self.camera.displayPreview(on: self.capturePreviewView)
}
}
func styleCaptureButton() {
captureButton.layer.borderColor = UIColor.black.cgColor
captureButton.layer.borderWidth = 2
captureButton.layer.cornerRadius = min(captureButton.frame.width, captureButton.frame.height) / 2
}
styleCaptureButton()
configureCameraController()
}
}
extension CameraController {
@IBAction func toggleFlash(_ sender: UIButton) {
if camera.flashMode == .on {
camera.flashMode = .off
toggleFlashButton.setImage(#imageLiteral(resourceName: "Flash Off Icon"), for: .normal)
} else {
camera.flashMode = .on
toggleFlashButton.setImage(#imageLiteral(resourceName: "Flash On Icon"), for: .normal)
}
}
@IBAction func switchCameras(_ sender: UIButton) {
do {
try camera.switchCameras()
} catch {
print(error)
}
switch camera.currentCameraPosition {
case .some(.front): toggleCameraButton.setImage(#imageLiteral(resourceName: "Front Camera Icon"), for: .normal)
case .some(.rear): toggleCameraButton.setImage(#imageLiteral(resourceName: "Rear Camera Icon"), for: .normal)
case .none: return
}
}
@IBAction func captureImage(_ sender: UIButton) {
camera.captureImage {(image, error) in
guard let image = image else {
print(error ?? "Image capture error")
return
}
try? PHPhotoLibrary.shared().performChangesAndWait {
PHAssetChangeRequest.creationRequestForAsset(from: image)
}
}
}
}
@QasymDar1126
Copy link

what were u using this code for?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment