Search code examples
iosswiftuiviewcontrolleruiimagepickercontroller

How can I take picture when the camera is already running


I have an app that has a snapchat type camera where the UIView displays the back camera. I have a button on top and when I click that button I would like to take a picture. Right now when I click that button it simply opens up another camera.

This is the code for the button click:

@IBAction func takePhoto(_ sender: UIButton) {
    imagePicker =  UIImagePickerController()
    imagePicker.delegate = self
    imagePicker.sourceType = .camera
    present(imagePicker, animated: true, completion: nil)
}

However, as stated above, that is redundant since my ViewController displays a camera on ViewDidAppear.

override func viewDidAppear(_ animated: Bool) {
    self.ShowCamera(self.frontCamera)
    fullView.isHidden = false
}

func ShowCamera(_ front: Bool) {
    self.captureSession.sessionPreset = AVCaptureSession.Preset.photo

    if let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [ .builtInWideAngleCamera,.builtInMicrophone],
                                                                mediaType: AVMediaType.video, position: .back).devices.first {
        self.captureDevice = availableDevices
        if captureSession.isRunning != true {
            self.beginSession()
        }
    }

    if self.captureDevice == nil {
        print("capture device is nil")
        return
    }

    do {
        try self.captureSession.removeInput(AVCaptureDeviceInput(device: self.captureDevice!))
    } catch let error as NSError {
        print(error)
    }
}

func beginSession() {
    do {
        let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
        captureSession.addInput(captureDeviceInput)
    } catch {
        print(error.localizedDescription)
    }

    captureSession.startRunning()

    let preview = AVCaptureVideoPreviewLayer(session: captureSession)
    self.previewLayer = preview
    preview.videoGravity = AVLayerVideoGravity.resizeAspectFill
    CameraView.layer.insertSublayer(self.previewLayer, at: 0)
    self.previewLayer.frame = self.CameraView.layer.frame

    let dataOutput = AVCaptureVideoDataOutput()
    dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_32BGRA)] as [String : Any]
    dataOutput.alwaysDiscardsLateVideoFrames = true

    if captureSession.canAddOutput(dataOutput)
    {
        captureSession.addOutput(dataOutput)
    }

    captureSession.commitConfiguration()
}

All the code above simply gets the UIView and shows the camera. The button for TakePhoto is a sublayer that shows on top of the camera image. When I click that button I want to use whatever image is displaying on my camera.


Solution

  • The command to capture a photo from the running session is

    guard let output = captureSession.outputs[0] as? AVCapturePhotoOutput 
    else {return}
    output.capturePhoto(with: settings, delegate: self)
    

    Here, self, is a AVCapturePhotoCaptureDelegate. You then receive the photo thru the delegate messages and extract and save it.