How to create a button to change the camera view in Swift?

10,540

The problem lies where you are setting the camera source.

You are setting it in viewDidAppear which will only be called when the view appears on the device. This is whenever you navigate to that view controller from another or close a presented view controller that is being presented by this one.

My suggestion would be to move the camera selection code into its own function that is called by both viewDidLoad and also whenever the changeCamera action is called.

@IBAction func changeCamera(sender: AnObject?) {
    camera = !camera

    reloadCamera()
}


func viewDidAppear(animated: Bool) {
    // normal code

    reloadCamera()
}

func reloadCamera() {
    // camera loading code
    captureSession = AVCaptureSession()
    captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
    var captureDevice:AVCaptureDevice! = nil
    // var backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    if (camera == false) {
        let videoDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)


        for device in videoDevices{
            let device = device as AVCaptureDevice
            if device.position == AVCaptureDevicePosition.Front {
                captureDevice = device
                break
            }
        }
    } else {
        var captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    }

    var error: NSError?
    var input = AVCaptureDeviceInput(device: captureDevice, error: &error)

    if error == nil && captureSession!.canAddInput(input) {
    captureSession!.addInput(input)

    stillImageOutput = AVCaptureStillImageOutput()
    stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
    if captureSession!.canAddOutput(stillImageOutput) {
        captureSession!.addOutput(stillImageOutput)

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer!.videoGravity = AVLayerVideoGravityResizeAspect
        previewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
        previewView.layer.addSublayer(previewLayer)

        captureSession!.startRunning()
    }
}

Also, an additional improvement would be to use a custom enum to store which camera is currently in use rather than a Boolean. This means you can add to it later say if there was ever a third camera. This would look like:

enum CameraType {
    case front
    case back
}

var camera = CameraType.back

I hope this helps, apologies for omitting the full code examples, currently on an iPad but I'll update when I get to a computer.


Update

Make sure you remove the previous preview layer from the view before you change the camera.

func reloadCamera() {
    captureSession?.stopRunning()
    previewLayer?.removeFromSuperlayer()

    // The rest of the camera loading code...

This should fix your camera freezing issue.

Share:
10,540
mindfreek
Author by

mindfreek

Hey there, I am a student Creative Technology. I love to fiddle around with Arduino, creating websites and I love good design. In 2015, my ambition is to learn Swift, a new coding language to make apps for the iPhone.

Updated on June 27, 2022

Comments

  • mindfreek
    mindfreek almost 2 years

    I would like to know how I could change the camera view when I press a button. At the moment, I am using a boolean var camera = false and when I press a button, I want to change the value to true and get the other camera. But that is not working. I now have this:

     @IBAction func changeCamera(sender: AnyObject) {
    
        camera = true
    
    }
    
    override func viewWillAppear(animated: Bool) {
    
        captureSession = AVCaptureSession()
        captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
        var captureDevice:AVCaptureDevice! = nil
        //var backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        if (camera == false){
        let videoDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
    
    
        for device in videoDevices{
            let device = device as AVCaptureDevice
            if device.position == AVCaptureDevicePosition.Front {
                captureDevice = device
                break
            }
        }
        } else {
            var captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    
        }
    
        var error: NSError?
        var input = AVCaptureDeviceInput(device: captureDevice, error: &error)
    
        if error == nil && captureSession!.canAddInput(input) {
            captureSession!.addInput(input)
    
            stillImageOutput = AVCaptureStillImageOutput()
            stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
            if captureSession!.canAddOutput(stillImageOutput) {
                captureSession!.addOutput(stillImageOutput)
    
                previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                previewLayer!.videoGravity = AVLayerVideoGravityResizeAspect
                previewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
                previewView.layer.addSublayer(previewLayer)
    
                captureSession!.startRunning()
            }
        }
    
    }
    
  • mindfreek
    mindfreek about 9 years
    Oooh, of course! The viewdidappear is only called once! I am very new to swift, so my apologies. Thank you so much for your awesome answer. I am going to try it out. I have never seen an Enum, so I will try it out. :)
  • Elliott Minns
    Elliott Minns about 9 years
    No need to apologise, learning is part of the fun and finding problems like this is the only way to learn :)
  • mindfreek
    mindfreek about 9 years
    Okay, I tried it, but something goes wrong... There is not an error, but the preview screen just freezes. Could you help me? And I have no idea how to implement you enum here.
  • mindfreek
    mindfreek about 9 years
    Dear Elliot Minns. Your is almost completed. Do you have any idea why the view with the camera would freeze when I try to change the view? You would really help me a lot! An maybe even other people trying this. I have no clue and I am a beginner... :(
  • Elliott Minns
    Elliott Minns about 9 years
    Sorry for not responding sooner, I was out of the country. I'll have a look in the next hour and get it working for you. Will update my answer shortly.
  • Elliott Minns
    Elliott Minns about 9 years
    Updated. Let me know if there are any other issues.
  • mindfreek
    mindfreek about 9 years
    Yes! It is working! But I did it a little different: @IBAction func changeCameraAction(sender: AnyObject) { self.camera = !camera reloadCamera() println("tried to change") previewLayer!.frame = previewView.bounds } Also good right?
  • Elliott Minns
    Elliott Minns about 9 years
    Yep, good spot. You would also need to set the previewLayer's frame. Glad you got it solved! Sorry I wasn't about earlier to help.
  • John Doe
    John Doe over 8 years
    For some reason enum doesn't work. It just gives me an error. What's going on?
  • Elliott Minns
    Elliott Minns over 8 years
    There's been changes in swift 1.2 to the way you declare enums, I shall make an update, but you need to use the keyword case
  • chengsam
    chengsam almost 8 years
    You should remove the comma in enum: enum CameraType { case Front case Back }