Switch between front and back camera while recording a video (Snap / Stories - Instagram) - ios

I created a custom camera, and I am trying to add a feature where users can switch between the front and back camera while recording a video.
But when trying to switch cameras, in the switch function the same pause and execute another video, not allowing me to get them together in a single file and play it at the end without cuts.
import UIKit
import AVFoundation
import CoreMedia
class VideoViewController: UIViewController, AVCaptureFileOutputRecordingDelegate,UITextFieldDelegate,UITextViewDelegate {
#IBOutlet weak var previewView:UIView!
#IBOutlet weak var recordButton: UIButton!
#IBOutlet weak var toggleButton: UIButton!
var captureSession = AVCaptureSession()
var videoCaptureDevice:AVCaptureDevice?
var previewLayer:AVCaptureVideoPreviewLayer?
var movieFileOutput = AVCaptureMovieFileOutput()
var outputFileLocation:URL?
override func viewDidLoad() {
super.viewDidLoad()
self.initializeCamera()
}
func dismissKeybord (){
textView.resignFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillLayoutSubviews() {
self.setVideoOrientation()
}
#IBAction func recordVideoButtonPressed(_ sender: AnyObject) {
if self.movieFileOutput.isRecording {
switchcamera = true
self.movieFileOutput.stopRecording()
} else {
self.movieFileOutput.connection(withMediaType: AVMediaTypeVideo).videoOrientation = self.videoOrientation()
self.movieFileOutput.maxRecordedDuration = self.maxRecordedDuration()
self.movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath:self.videoFileLocation()), recordingDelegate: self)
}
self.updateRecordButtonTitle()
}
#IBAction func cameraTogglePressed(_ sender: AnyObject) {
self.switchCameraInput()
}
#IBAction func btnGallery(_ sender: UIButton) {
self.performSegue(withIdentifier: "videoPreview", sender: nil)
}
func initializeCamera(){
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh
let discovery = AVCaptureDeviceDiscoverySession.init(deviceTypes: [AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified) as AVCaptureDeviceDiscoverySession
for device in discovery.devices as [AVCaptureDevice] {
if device.hasMediaType(AVMediaTypeVideo) {
if device.position == AVCaptureDevicePosition.back {
self.videoCaptureDevice = device
}
}
}
if videoCaptureDevice != nil {
do {
try self.captureSession.addInput(AVCaptureDeviceInput(device: self.videoCaptureDevice))
if let audioInput = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) {
try self.captureSession.addInput(AVCaptureDeviceInput(device: audioInput))
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.previewView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.previewView.layer.addSublayer(self.previewLayer!)
self.previewLayer?.frame = self.previewView.frame
self.setVideoOrientation()
self.captureSession.addOutput(self.movieFileOutput)
self.captureSession.startRunning()
} catch {
print(error)
}
}
}
//Orientation Camera Record
func setVideoOrientation() {
if let connection = self.previewLayer?.connection {
if connection.isVideoOrientationSupported {
connection.videoOrientation = self.videoOrientation()
self.previewLayer?.frame = self.view.bounds
}
}
}
//Switch Camrea function
func switchCameraInput() {
self.captureSession.beginConfiguration()
var existingConnection:AVCaptureDeviceInput!
for connection in self.captureSession.inputs {
let input = connection as! AVCaptureDeviceInput
if input.device.hasMediaType(AVMediaTypeVideo) {
existingConnection = input
}
}
self.captureSession.removeInput(existingConnection)
var newCamera:AVCaptureDevice!
if let oldCamera = existingConnection {
if oldCamera.device.position == .back {
newCamera = self.cameraWithPosition(position: .front)
} else {
newCamera = self.cameraWithPosition(position: .back)
}
}
var newInput:AVCaptureDeviceInput!
do {
newInput = try AVCaptureDeviceInput(device: newCamera)
self.captureSession.addInput(newInput)
} catch {
print(error)
}
self.captureSession.commitConfiguration()
}
//Capture Function Archive Library
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
print("Finished recording: \(outputFileURL)")
self.outputFileLocation = outputFileURL
//self.performSegue(withIdentifier: "videoPreview", sender: nil)
}
// Video Orientation Record
func videoOrientation() -> AVCaptureVideoOrientation {
var videoOrientation:AVCaptureVideoOrientation!
let orientation:UIDeviceOrientation = UIDevice.current.orientation
switch orientation {
case .portrait:
videoOrientation = .portrait
case .landscapeRight:
videoOrientation = .landscapeLeft
case .landscapeLeft:
videoOrientation = .landscapeRight
case .portraitUpsideDown:
videoOrientation = .portraitUpsideDown
default:
videoOrientation = .portrait
}
return videoOrientation
}
//Video Save Temp Library
func videoFileLocation() -> String {
return NSTemporaryDirectory().appending("prompterFile.mov")
}
// Record Funcition
func updateRecordButtonTitle() {
var isRecording = false
if !self.movieFileOutput.isRecording {
isRecording = true
} else {
isRecording = false
stopScrolling()
//Open Button Gallery
btnGallery.isHidden = false
}
}
func maxRecordedDuration() -> CMTime {
let seconds : Int64 = 300
let preferredTimeScale : Int32 = 1
return CMTimeMake(seconds, preferredTimeScale)
}
func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice?
{
let discovery = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: .unspecified) as AVCaptureDeviceDiscoverySession
for device in discovery.devices as [AVCaptureDevice] {
if device.position == position {
return device
}
}
return nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let preview = segue.destination as! VideoPreviewViewController
preview.fileLocation = self.outputFileLocation
}
}

Related

implementing camera switch (front to back) upon button touch

I'm having trouble implementing "camera switch" upon button touch.
I'm still new to Swift and iOS developement, so most of the code I've currently implemented has been via tutorials.
I tried to implement the camera switch on my own.
However it's leading to this error:
Multiple audio/video AVCaptureInputs are not currently supported
I understand the error, but not sure how I would correctly implement the switch function within my code.
Here is the switch/flip function:
#IBAction func FlipCamera(_ sender: Any) {
captureSession.stopRunning()
cameraPreviewlayer?.removeFromSuperlayer()
//cameraPreviewlayer = nil
//self.captureSession = nil
setupCaptureSession()
if currentCamera! == backCamera{
//print(currentCamera)
currentCamera = frontCamera}
else {
currentCamera = backCamera}
setupCaptureSession()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
Here is the class in its entirety:
class CameraViewController :UIViewController {
func applyRoundCorner(_ object: AnyObject){
object.layer.cornerRadius = (object.frame.size.width)/2
object.layer.masksToBounds = true
}
#IBOutlet weak var cameraButton: UIButton!
#IBOutlet weak var imagePreview: UIImageView!
#IBAction func Library(_ sender: Any) {
}
#IBAction func FlipCamera(_ sender: Any) {
captureSession.stopRunning()
cameraPreviewlayer?.removeFromSuperlayer()
//cameraPreviewlayer = nil
//self.captureSession = nil
setupCaptureSession()
if currentCamera! == backCamera{
//print(currentCamera)
currentCamera = frontCamera}
else {
currentCamera = backCamera}
setupCaptureSession()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var cameraPreviewlayer: AVCaptureVideoPreviewLayer?
var photoOutput: AVCapturePhotoOutput?
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
self.applyRoundCorner(cameraButton)
setupCaptureSession()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
func setupCaptureSession(){
captureSession.sessionPreset = AVCaptureSession.Preset.photo // Why exclamation point?
let availableDevice = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: .video, position: .unspecified)
let devices = availableDevice.devices //back or front
for device in devices {
if device.position == AVCaptureDevice.Position.back{
backCamera = device
}else if device.position == AVCaptureDevice.Position.front{
frontCamera = device
}
}
currentCamera = backCamera
}
func setupInputOutput(){
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey:AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
}
catch{
print(error)
}
}
func setupPreviewLayer(){
cameraPreviewlayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewlayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewlayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewlayer?.frame = self.imagePreview.frame
self.view.layer.insertSublayer(cameraPreviewlayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
#IBAction func TakePhoto(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "showPhotoSegue"{
let previewVC = segue.destination as! PreviewViewController
previewVC.image = self.image
}
}
}
extension CameraViewController: AVCapturePhotoCaptureDelegate{
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation(){
image = UIImage(data: imageData)
performSegue(withIdentifier:"showPhotoSegue", sender: nil) }
}
}

Swift How to take a photo and save with same button

I am a beginner.
I have a question....
How can I write the code so that every time the photo is snap it will be saved to the photo gallery. I just want to logic that if man press the taking a photo button, right save the photo and if i want to see the photo, i have to go to gallery. thanks for reading me.
class ViewController: UIViewController {
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
func setupCaptureSession(){
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
func setupDevice(){
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let device = deviceDiscoverySession.devices
for device in device {
if device.position == AVCaptureDevice.Position.back {
backCamera = device
} else if device.position == AVCaptureDevice.Position.front {
frontCamera = device
}
currentCamera = backCamera
}
}
func setupInputOutput(){
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
} catch {
print(error)
}
}
func setupPreviewLayer(){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
#IBAction func cameraButton_TouchIpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: self)
// performSegue(withIdentifier: "showPhoto_Segue", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPhoto_Segue" {
let previewVC = segue.destination as! PreviewViewController
previewVC.image = self.image
}
}
}
extension ViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation() {
print(imageData)
image = UIImage(data: imageData)
performSegue(withIdentifier: "showPhoto_Segue", sender: nil)
}
}
} // this is main ViewController.
import UIKit
class PreviewViewController: UIViewController {
#IBOutlet weak var photo: UIImageView!
var image: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
photo.image = self.image
// Do any additional setup after loading the view.
}
#IBAction func cancelButton_TouchUpInside(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func saveButton_YouvhUpInside(_ sender: Any) {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
dismiss(animated: true, completion: nil)
}
} // this is second ViewController

How to change some function to be compatible for iOS 10 or below for some function in my snapchat like camera view controller

I am making a view controller to make a camera view controller like snapchat camera. my code below is worked perfectly for iOS 11 or above. to be honest, I don't really grasp my code since i just follow along the tutorial for this snapchat like camera view controller
import UIKit
import AVFoundation
import SVProgressHUD
class CameraVC: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var cameraButton: DesignableButton!
#IBOutlet weak var retryButton: DesignableButton!
// to receive data from MainMenuVC
var employeeData : Employee?
var checkinData = CheckIn()
var captureSession = AVCaptureSession()
// which camera input do we want to use
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
// to keep track which camera do we use currently
var currentDevice: AVCaptureDevice?
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
var toggleCameraGestureRecognizer = UISwipeGestureRecognizer()
var zoomInGestureRecognizer = UISwipeGestureRecognizer()
var zoomOutGestureRecognizer = UISwipeGestureRecognizer()
var thereIsAnError : Bool = false {
didSet {
if thereIsAnError {
cameraButton.isHidden = true
cameraButton.isEnabled = false
retryButton.isHidden = false
retryButton.isEnabled = true
} else {
cameraButton.isHidden = false
cameraButton.isEnabled = true
retryButton.isHidden = true
retryButton.isEnabled = false
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
getDateTimeFromServer()
// initial value
thereIsAnError = false
timeLabel.text = ""
dateLabel.text = ""
cameraButton.isEnabled = false
cameraButton.alpha = 0.4
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
setGestureRecognizer()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if checkinData.dateTime != nil {
SVProgressHUD.dismiss()
}
}
#IBAction func shutterButtonDidPressed(_ sender: Any) {
// when the button is pressed, we capture the image and set the photoOutput
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: self)
// perform segue is below in the AVCapturePhotoCaptureDelegate
}
#IBAction func retryButtonDidPressed(_ sender: Any) {
if checkinData.dateTime == nil {
getDateTimeFromServer()
}
}
}
extension CameraVC {
// MARK: - Helper Methods
// MARK: - Helper Methods
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCheckinDetail" {
let checkinDetailTVC = segue.destination as! CheckinDetailVC
checkinDetailTVC.dataOfCheckin = checkinData
checkinDetailTVC.dataOfEmployee = employeeData
// to set the navbar back button title in the checkinDetailVC
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}
func getDateTimeFromServer() {
SVProgressHUD.show(withStatus: "Loading Data")
NetworkingService.getCurrentTimeFromServer { (result) in
switch result {
case .failure:
self.thereIsAnError = true
SVProgressHUD.dismiss()
self.showAlert(alertTitle: "Sorry", alertMessage: "Internet connection issue, please tap the retry button.", actionTitle: "Back")
case .success(let timeFromServer) :
guard let stringDateTimeServer = timeFromServer as? String else {return}
self.checkinData.dateTime = stringDateTimeServer
let dateTimeService = DateTimeService(fromDateTimeString: stringDateTimeServer)
let time = dateTimeService.parsingDateAndTime()?.timeOnly
self.timeLabel.text = "\(time ?? "-")"
self.dateLabel.text = DateTimeService.changeFormat(of: stringDateTimeServer, toFormat: "dd MMM yyyy")
self.cameraButton.isEnabled = true
self.cameraButton.alpha = 1
self.thereIsAnError = false
SVProgressHUD.dismiss()
}
}
}
func setGestureRecognizer() {
// change camera from front to back
toggleCameraGestureRecognizer.direction = .up
toggleCameraGestureRecognizer.addTarget(self, action: #selector(self.switchCamera))
view.addGestureRecognizer(toggleCameraGestureRecognizer)
// Zoom In recognizer
zoomInGestureRecognizer.direction = .right
zoomInGestureRecognizer.addTarget(self, action: #selector(zoomIn))
view.addGestureRecognizer(zoomInGestureRecognizer)
// Zoom Out recognizer
zoomOutGestureRecognizer.direction = .left
zoomOutGestureRecognizer.addTarget(self, action: #selector(zoomOut))
view.addGestureRecognizer(zoomOutGestureRecognizer)
}
func setupCaptureSession() {
// to specify image resolution and quality we want, we set to the highest resolution possible
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
func setupDevice() {
// to decide whether we use front or back camer
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices {
if device.position == AVCaptureDevice.Position.back {
backCamera = device
} else if device.position == AVCaptureDevice.Position.front {
frontCamera = device
}
}
// default device
currentDevice = frontCamera
}
func setupInputOutput() {
// after the camera capture that image (input), we generate the image DATA (output)
// put the input and output to capture Session
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
} catch {
print(error)
}
}
func setupPreviewLayer() {
// to display image data on the screen
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
#objc func switchCamera() {
captureSession.beginConfiguration()
// Change the device based on the current camera
let newDevice = (currentDevice?.position == AVCaptureDevice.Position.back) ? frontCamera : backCamera
// Remove all inputs from the session
for input in captureSession.inputs {
captureSession.removeInput(input as! AVCaptureDeviceInput)
}
// Change to the new input
let cameraInput:AVCaptureDeviceInput
do {
cameraInput = try AVCaptureDeviceInput(device: newDevice!)
} catch {
print(error)
return
}
if captureSession.canAddInput(cameraInput) {
captureSession.addInput(cameraInput)
}
currentDevice = newDevice
captureSession.commitConfiguration()
}
#objc func zoomIn() {
if let zoomFactor = currentDevice?.videoZoomFactor {
if zoomFactor < 5.0 {
let newZoomFactor = min(zoomFactor + 1.0, 5.0)
do {
try currentDevice?.lockForConfiguration()
currentDevice?.ramp(toVideoZoomFactor: newZoomFactor, withRate: 1.0)
currentDevice?.unlockForConfiguration()
} catch {
print(error)
}
}
}
}
#objc func zoomOut() {
if let zoomFactor = currentDevice?.videoZoomFactor {
if zoomFactor > 1.0 {
let newZoomFactor = max(zoomFactor - 1.0, 1.0)
do {
try currentDevice?.lockForConfiguration()
currentDevice?.ramp(toVideoZoomFactor: newZoomFactor, withRate: 1.0)
currentDevice?.unlockForConfiguration()
} catch {
print(error)
}
}
}
}
func startRunningCaptureSession() {
// to start capturing the data
captureSession.startRunning()
}
}
extension CameraVC: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation() {
checkinData.photo = UIImage(data: imageData)
performSegue(withIdentifier: "goToCheckinDetail", sender: nil)
}
}
}
but when I set my deployment target to iOS 10.3, I got an error that said some method is only available for iOS 11 or newer.
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation() {
checkinData.photo = UIImage(data: imageData)
performSegue(withIdentifier: "goToCheckinDetail", sender: nil)
}
}
AVCapturePhoto' is only available on iOS 11.0 or newer
fileDataRepresentation()' is only available on iOS 11.0 or newer
and
func setupInputOutput() {
// after the camera capture that image (input), we generate the image DATA (output)
// put the input and output to capture Session
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
} catch {
print(error)
}
}
'jpeg' is only available on iOS 11.0 or newer
Please help me, I need some function that equal to those function for iOS 10 (at least) or below.
Create an AVCapturePhotoOutput object. Use its properties to determine supported capture settings and to enable certain features (for example, whether to capture Live Photos).
fileprivate var photoOutput: AVCapturePhotoOutput!
Create and configure an AVCapturePhotoSettings object to choose
features and settings for a specific capture (for example, whether to enable image stabilization or flash).
photoOutput = AVCapturePhotoOutput()
if self.session.canAddOutput(photoOutput) {
self.session.addOutput(photoOutput)
}
Capture an image by passing your photo settings object to the
capturePhoto(with:delegate:) method along with a delegate object implementing the AVCapturePhotoCaptureDelegate protocol. The photo capture output then calls your delegate to notify you of significant events during the capture process.
queue.async { self.photoOutput.capturePhoto(with: AVCapturePhotoSettings(), delegate: self) }

Why does calling .capturePhoto(with:,delegate:) on AVCapturePhotoOutput cause a crash

I have been struggling with this error for a few days where if I take picture segue screens, dismiss the current screen flip the camera and take a picture the app crashes with an error.
[AVCapturePhotoOutput capturePhotoWithSettings:delegate:] No active and enabled video connection'
I have narrowed the error down to three lines of code, although I am not sure how to fix it I believe the solution has something to do with my AVCapturePhotoSettings
This is where I have narrowed down the trouble code to:
#IBAction func cameraButton_TouchUpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutPut?.capturePhoto(with: settings, delegate: self as AVCapturePhotoCaptureDelegate)
}
Have I set up the settings incorrectly. Thank you
Here is the code in its entirety
import UIKit
import AVFoundation
protocol previewSegueDelegate {
func previewSegueDelegate(image:UIImage,device:AVCaptureDevice)
}
class MainCameraCollectionViewCell: UICollectionViewCell {
var gdelegate: gestureDelegate?
var pdelegate: previewSegueDelegate?
#IBOutlet weak var myView: UIView!
var captureSession = AVCaptureSession()
private var sessionQueue: DispatchQueue!
var captureConnection = AVCaptureConnection()
var currentCamera: AVCaptureDevice?
var photoOutPut: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
var image: UIImage?
var usingFrontCamera = false
override func awakeFromNib() {
super.awakeFromNib()
setupCaptureSession()
setupDevice()
setupInput()
setupPreviewLayer()
startRunningCaptureSession()
print("Inside of camera cell")
}
func setupCaptureSession(){
captureSession.sessionPreset = AVCaptureSession.Preset.photo
sessionQueue = DispatchQueue(label: "session queue")
}
func setupDevice(usingFrontCamera:Bool = false){
sessionQueue.async {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices{
if usingFrontCamera && device.position == AVCaptureDevice.Position.front {
self.currentCamera = device
} else if device.position == AVCaptureDevice.Position.back {
self.currentCamera = device
}
}
}
}
func setupInput() {
sessionQueue.async {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: self.currentCamera!)
if self.captureSession.canAddInput(captureDeviceInput) {
self.captureSession.addInput(captureDeviceInput)
}
self.photoOutPut = AVCapturePhotoOutput()
self.photoOutPut?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
if self.captureSession.canAddOutput(self.photoOutPut!) {
self.captureSession.addOutput(self.photoOutPut!)
}
} catch {
print(error)
}
}
}
func setupPreviewLayer(){
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
#IBAction func cameraButton_TouchUpInside(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutPut?.capturePhoto(with: settings, delegate: self as AVCapturePhotoCaptureDelegate)
print("camera button touched")
}
#IBAction func FlipThe_camera(_ sender: UIButton) {
print("Flip Touched")
captureSession.beginConfiguration()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
captureSession.removeInput(input)
}
}
usingFrontCamera = !usingFrontCamera
setupCaptureSession()
setupDevice(usingFrontCamera: usingFrontCamera)
setupInput()
captureSession.commitConfiguration()
startRunningCaptureSession()
}
}
extension MainCameraCollectionViewCell: AVCapturePhotoCaptureDelegate{
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation(){
print(imageData)
image = UIImage(data: imageData)
if(self.image == nil){
print("The image is empty")
}
pdelegate?.previewSegueDelegate(image: self.image!, device: currentCamera!)
}
}
}
The logic of camera switch is wrong.
#IBAction func FlipThe_camera(_ sender: UIButton) {
print("Flip Touched")
captureSession.beginConfiguration()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
captureSession.removeInput(input)
}
}
usingFrontCamera = !usingFrontCamera
setupCaptureSession()
setupDevice(usingFrontCamera: usingFrontCamera)
setupInput()
captureSession.commitConfiguration()
startRunningCaptureSession()
}
You should not add output again in setupInput() .
And should not start running session again in startRunningCaptureSession()

BarCodeScanning Not Working Swift iOS 11

Before you jump and tell me its a duplicate or send me links check out the code.
The camera shows up but the delegate for output is not called.
import AVFoundation
import UIKit
protocol ScannerViewControllerOutputDelegate : class {
func scannerOutput(scannedString: String?)
}
class ScannerViewController: UIViewController {
public weak var delegate: ScannerViewControllerOutputDelegate?
#IBOutlet private weak var cameraView: UIView!
#IBOutlet private weak var lblDataType: UILabel!
#IBOutlet private weak var lblDataInfo: UILabel!
private let captureSession: AVCaptureSession = AVCaptureSession()
private var previewLayer: AVCaptureVideoPreviewLayer!
private var presenter: ScannerViewPresenter?
deinit {
captureSession.stopRunning()
presenter = nil
delegate = nil
}
#IBAction func didTapCancelButton(_ sender: UIBarButtonItem) {
print("ScannerViewController -> USER CANCLED: will hide scannerview controller")
captureSession.stopRunning()
dismiss(animated: true) {
print("ScannerViewController -> USER CANCLED: did hide scannerview controller")
self.delegate?.scannerOutput(scannedString: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
presenter = ScannerViewPresenter(withController: self)
self.modalPresentationStyle = .formSheet
view.backgroundColor = .eify_darkGray
guard let videoInput = initializeDeviceCamera() else {
presenter?.deviceNotSupportedAlert()
return
}
guard captureSession.add(captureInput: videoInput) else {
presenter?.deviceNotSupportedAlert()
return
}
guard captureSession.addMetadataOutput(withDelegate: self, andQueue: .global()) else {
presenter?.deviceNotSupportedAlert()
return
}
setupScanPreview()
captureSession.startRunning()
}
fileprivate func initializeDeviceCamera() -> AVCaptureDeviceInput? {
guard let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
presenter?.deviceNotSupportedAlert()
return nil
}
do {
let videoInput: AVCaptureDeviceInput
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
return videoInput
} catch let outError as NSError {
presenter?.deviceNotSupportedAlert()
print("ScannerViewController -> Device Not Supported: \(outError.description)")
return nil
}
}
fileprivate func setupScanPreview() {
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = cameraView.layer.bounds
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
view.layer.addSublayer(previewLayer)
}
fileprivate func stopSession() {
if (captureSession.isRunning == true) {
captureSession.stopRunning()
}
}
fileprivate func startSession() {
if (captureSession.isRunning == false) {
captureSession.startRunning()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startSession()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopSession()
}
fileprivate func found(code: String?) {
delegate?.scannerOutput(scannedString: code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .all
}
}
extension ScannerViewController : AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: readableObject.stringValue)
}
dismiss(animated: true)
}
}
extension AVCaptureSession {
func add(captureInput videoInput: AVCaptureDeviceInput) -> Bool {
if self.canAddInput(videoInput) {
self.addInput(videoInput)
return true
}
return false
}
func addMetadataOutput(withDelegate delegate: AVCaptureMetadataOutputObjectsDelegate, andQueue queue: DispatchQueue) -> Bool {
let metadataOutput = AVCaptureMetadataOutput()
if self.canAddOutput(metadataOutput) {
metadataOutput.setMetadataObjectsDelegate(delegate, queue: queue)
if metadataOutput.canAddObject(ofType: .qr) {
metadataOutput.metadataObjectTypes = [.qr]
}
self.addOutput(metadataOutput)
return true
}
return false
}
}

Resources