Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double Tap or Use Button To Switch Camera From Back to Front (Xcode 8, Swift 3)

Tags:

ios

swift3

So, lately I have been trying to implement the function of switching the camera view from back to front camera in Swift 3. However, with no luck.

Currently, my default view is from the back camera - I can take pictures with it and then retake. But can anyone help me and show how do I either double tap the screen to switch cameras or simply use the assigned button to switch them? Thank you!

import UIKit
import AVFoundation
import FirebaseDatabase

class CameraView: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

override var prefersStatusBarHidden: Bool {
    return true
}

var captureSession : AVCaptureSession!
var stillImageOutput : AVCaptureStillImageOutput!
var previewLayer : AVCaptureVideoPreviewLayer!

@IBOutlet var cameraView: UIView!
override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    previewLayer?.frame = cameraView.bounds
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    captureSession = AVCaptureSession()
    captureSession?.sessionPreset = AVCaptureSessionPreset1920x1080

    var backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
    var error : NSError?

    do {
        var input = try! AVCaptureDeviceInput(device: backCamera)
        if (error == nil && captureSession?.canAddInput(input) != nil) {

            captureSession?.addInput(input)

            stillImageOutput = AVCaptureStillImageOutput()
            stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

            if (captureSession?.canAddOutput(stillImageOutput) != nil) {

                captureSession?.addOutput(stillImageOutput)

                previewLayer = AVCaptureVideoPreviewLayer (session: captureSession)
                previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
                previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
                cameraView.layer.addSublayer(previewLayer)
                captureSession?.startRunning() }
        }

    } catch {

    }
}

@IBOutlet var tempImageView: UIImageView!
@IBAction func didPressTakePhoto(_ sender: UIButton) {

    if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {

        videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
        stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: {

            (sampleBuffer, error) in
            if sampleBuffer != nil {

                var imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
                var dataProvider = CGDataProvider.init(data: imageData as! CFData)
                var cgImageRef = CGImage.init(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)

                var image = UIImage (cgImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.right)

                self.tempImageView.image = image
                self.tempImageView.isHidden = false

            }

        })

    }
}

var didTakePhoto = Bool()

@IBAction func didPressTakeAnother(_ sender: UIButton) {

    if didTakePhoto == true {

        tempImageView.isHidden = true
        didTakePhoto = false

    } else {

        captureSession?.startRunning()
        didTakePhoto = true

    }
}
}
like image 435
Andriyas Redel Avatar asked Sep 26 '16 00:09

Andriyas Redel


2 Answers

don't see here any problems - here is working solution:

import Foundation
import UIKit
import AVFoundation

class MainViewController: UIViewController {

    var tempImage: UIImageView?

    var captureSession: AVCaptureSession?
    var stillImageOutput: AVCaptureStillImageOutput?
    var videoPreviewLayer: AVCaptureVideoPreviewLayer?
    var currentCaptureDevice: AVCaptureDevice?

    var usingFrontCamera = false


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        loadCamera()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        videoPreviewLayer!.frame = self.cameraPreviewSurface.bounds
    }

    @IBAction func switchButtonAction(_ sender: Any) {
        usingFrontCamera = !usingFrontCamera
        loadCamera()
    }


    func getFrontCamera() -> AVCaptureDevice?{
        let videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)


        for device in videoDevices!{
            let device = device as! AVCaptureDevice
            if device.position == AVCaptureDevicePosition.front {
                return device
            }
        }
        return nil
    }

    func getBackCamera() -> AVCaptureDevice{
        return AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
    }



    func loadCamera() {
        if(captureSession == nil){
            captureSession = AVCaptureSession()
            captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
        }
        var error: NSError?
        var input: AVCaptureDeviceInput!

        currentCaptureDevice = (usingFrontCamera ? getFrontCamera() : getBackCamera())

        do {
            input = try AVCaptureDeviceInput(device: currentCaptureDevice)
        } catch let error1 as NSError {
            error = error1
            input = nil
            print(error!.localizedDescription)
        }

        for i : AVCaptureDeviceInput in (self.captureSession?.inputs as! [AVCaptureDeviceInput]){
            self.captureSession?.removeInput(i)
        }
        if error == nil && captureSession!.canAddInput(input) {
            captureSession!.addInput(input)
            stillImageOutput = AVCaptureStillImageOutput()
            stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
            if captureSession!.canAddOutput(stillImageOutput) {
                captureSession!.addOutput(stillImageOutput)
                videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                videoPreviewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
                videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
                //self.cameraPreviewSurface.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
                self.cameraPreviewSurface.layer.addSublayer(videoPreviewLayer!)
                DispatchQueue.main.async {
                    self.captureSession!.startRunning()
                }


            }
        }



    }
}

some notes:

cameraPreviewSurface - this is your UIView where camera will show

don't reassign the session, don't just add input, but before add new - remove existing ones,

p.s. code done with swift 3.0.1 / xcode 8.1

Cheers )

like image 66
Stepan Maksymov Avatar answered Oct 21 '22 10:10

Stepan Maksymov


In addition to Stepan Maksymov post, I sugest to add this function

func stopCaptureSession () {
    self.captureSession.stopRunning()

    if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
        for input in inputs {
            self.captureSession.removeInput(input)
        }
    }

}

And call it instead of his post lines:

for i : AVCaptureDeviceInput in (self.captureSession?.inputs as! [AVCaptureDeviceInput]){
    self.captureSession?.removeInput(i)
}

This way the cameras will change quicker.

like image 24
lightbyte Avatar answered Oct 21 '22 11:10

lightbyte