Search code examples
iosswiftxcodecamera

Switching my cameras back/front with a Button


I currently have a working camera app inside my App and want to make the button call either the front or back camera device. I think I got most of the code for that in-place, the only thing I can't figure out is how to code the button to call the two devices.

I've tried some if statements, but that didn't get accepted by Xcode, still new to this code game, learning by doing, but sometimes I hit a bump, so any help would be greatly appreciated.

Here is my full camera code I use:

import Foundation
import UIKit
import AVFoundation

class CameraViewController: 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 devices = deviceDiscoverySession.devices

    for device in devices {
        if device.position == AVCaptureDevice.Position.back {
            backCamera = device
        } else if device.position == AVCaptureDevice.Position.front {
            frontCamera = device
        }
    }

    currentCamera = frontCamera

}

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(_ sender: Any) {
    let settings = AVCapturePhotoSettings()
    photoOutput?.capturePhoto(with: settings, delegate: self)
    // performSegue(withIdentifier: "previewCameraPhoto", sender: nil)
}

@IBAction func switchCamera(_ sender: Any) {
    // The button which should switch the Cameras back an forth.  
}



override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "previewCameraPhoto" {
        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: "previewCameraPhoto", sender: nil)
    }
}

override var prefersStatusBarHidden: Bool
{
    return true
}
}

Solution

  • you don't need to declare variables for backCamera, frontCamera, currentCamera, you only need to save variables for AVCaptureDeviceInput.

    try to comment:

    //    var backCamera: AVCaptureDevice?
    //    var frontCamera: AVCaptureDevice?
    //    var currentCamera: AVCaptureDevice?
    

    and add those:

    var frontCameraDeviceInput: AVCaptureDeviceInput?
    var backCameraDeviceInput: AVCaptureDeviceInput?
    

    your setupDevice function looks like this

    func setupDevice() {
        let frontCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
        let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
    
        frontCameraDeviceInput = try? AVCaptureDeviceInput(device: frontCamera!)
        backCameraDeviceInput = try? AVCaptureDeviceInput(device: backCamera!)
    }
    

    change setupInputOutput function to:

    func setupInputOutput() {
        captureSession.addInput(backCameraDeviceInput!)
        photoOutput = AVCapturePhotoOutput()
        photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format:[AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
        captureSession.addOutput(photoOutput!)
    }
    

    and finally your switchCamera

    @IBAction func switchCamera(_ sender: Any) {
            // The button which should switch the Cameras back an forth.
    
            captureSession.beginConfiguration()
            //Change camera device inputs from back to front or opposite
            if captureSession.inputs.contains(frontCameraDeviceInput!) == true {
                captureSession.removeInput(frontCameraDeviceInput!)
                captureSession.addInput(backCameraDeviceInput!)
            } else if captureSession.inputs.contains(backCameraDeviceInput!) == true {
                captureSession.removeInput(backCameraDeviceInput!)
                captureSession.addInput(frontCameraDeviceInput!)
            }
    
            //Commit all the configuration changes at once
            captureSession.commitConfiguration();
        }