Updated 27 September 2021
Accessing the built-in Image Picker Controller is a quick and easy way to get image and video capture into your app. However, when you need style and functionality that goes beyond the stock Image Picker Controller you will need to create a Custom Camera View.
Add the following view elements to the ViewController in Storyboard:
At the top of your ViewController file, import AVFoundation
Create Outlets for the UIView and UIImageView.
previewView
.captureImageView
.Create an Action for the UIButton.
didTakePhoto
.Above the viewDidLoad
method, where you create variables you want to be accessible anywhere in the ViewController file, create the following Instance Variables.
1 2 3 |
<span class="k">var</span> <span class="nv">captureSession</span><span class="p">:</span> <span class="kt">AVCaptureSession</span><span class="o">!</span><br> <span class="k">var</span> <span class="nv">stillImageOutput</span><span class="p">:</span> <span class="kt">AVCapturePhotoOutput</span><span class="o">!</span><br> <span class="k">var</span> <span class="nv">videoPreviewLayer</span><span class="p">:</span> <span class="kt">AVCaptureVideoPreviewLayer</span><span class="o">!</span> |
The bulk of the camera setup will happen in the viewDidAppear
.
super.viewDidAppear(animated)
also.
1 2 3 4 |
<span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidAppear</span><span class="p">(</span><span class="n">_</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewDidAppear</span><span class="p">(</span><span class="n">animated</span><span class="p">)</span> <span class="c1">// Setup your camera here...</span> <span class="p">}</span> |
The session will coordinate the input and output data from the device’s camera.
Still in viewDidAppear
1 2 |
<span class="n">captureSession</span> <span class="o">=</span> <span class="kt">AVCaptureSession</span><span class="p">()</span> <span class="n">captureSession</span><span class="o">.</span><span class="n">sessionPreset</span> <span class="o">=</span> <span class="o">.</span><span class="n">medium</span> |
AVCaptureSession.Preset.High
or AVCaptureSession.Preset.medium
to keep the size under the 10mb Parse max.In this example, we will be using the rear camera. The front camera and microphone are additional input devices at your disposal. Printing debug comment incase the fetching the rear camera fails. Still in viewDidAppear
1 2 3 4 5 |
<span class="k">guard</span> <span class="k">let</span> <span class="nv">backCamera</span> <span class="o">=</span> <span class="kt">AVCaptureDevice</span><span class="o">.</span><span class="nf">default</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="kt">AVMediaType</span><span class="o">.</span><span class="n">video</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Unable to access back camera!"</span><span class="p">)</span> <span class="k">return</span> <span class="p">}</span> |
We now need to make an AVCaptureDeviceInput. The AVCaptureDeviceInput will serve as the “middle man” to attach the input device, backCamera
to the session.
backCamera
input device.try
catch
to handle any potential errors we might encounter. In Objective C, errors will be using the traditional NSError pattern. Still in viewDidAppear
1 2 3 4 5 6 7 |
<span class="k">do</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">input</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">AVCaptureDeviceInput</span><span class="p">(</span><span class="nv">device</span><span class="p">:</span> <span class="n">backCamera</span><span class="p">)</span> <span class="c1">//Step 9</span> <span class="p">}</span> <span class="k">catch</span> <span class="k">let</span> <span class="nv">error</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Error Unable to initialize back camera: </span><span class="se">\(</span><span class="n">error</span><span class="o">.</span><span class="n">localizedDescription</span><span class="se">)</span><span class="s">"</span><span class="p">)</span> <span class="p">}</span> |
Just like we created an AVCaptureDeviceInput to be the “middle man” to attach the input device, we will use AVCapturePhotoOutput to help us attach the output to the session.
1 |
<span class="n">stillImageOutput</span> <span class="o">=</span> <span class="kt">AVCapturePhotoOutput</span><span class="p">()</span> |
If there are no errors from our last step and the session is able to accept input and output, the go-ahead and add input add output to the Session.
1 2 3 4 5 6 7 |
<span class="n">stillImageOutput</span> <span class="o">=</span> <span class="kt">AVCapturePhotoOutput</span><span class="p">()</span> <span class="k">if</span> <span class="n">captureSession</span><span class="o">.</span><span class="nf">canAddInput</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="o">&&</span> <span class="n">captureSession</span><span class="o">.</span><span class="nf">canAddOutput</span><span class="p">(</span><span class="n">stillImageOutput</span><span class="p">)</span> <span class="p">{</span> <span class="n">captureSession</span><span class="o">.</span><span class="nf">addInput</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="n">captureSession</span><span class="o">.</span><span class="nf">addOutput</span><span class="p">(</span><span class="n">stillImageOutput</span><span class="p">)</span> <span class="nf">setupLivePreview</span><span class="p">()</span> <span class="p">}</span> |
Now that the input and output are all hooked up with our session, we just need to get our Live Preview going so we can actually display what the camera sees on the screen in our UIView, previewView
.
previewView
1 2 3 4 5 6 7 8 9 10 |
<span class="kd">func</span> <span class="nf">setupLivePreview</span><span class="p">()</span> <span class="p">{</span> <span class="n">videoPreviewLayer</span> <span class="o">=</span> <span class="kt">AVCaptureVideoPreviewLayer</span><span class="p">(</span><span class="nv">session</span><span class="p">:</span> <span class="n">captureSession</span><span class="p">)</span> <span class="n">videoPreviewLayer</span><span class="o">.</span><span class="n">videoGravity</span> <span class="o">=</span> <span class="o">.</span><span class="n">resizeAspect</span> <span class="n">videoPreviewLayer</span><span class="o">.</span><span class="n">connection</span><span class="p">?</span><span class="o">.</span><span class="n">videoOrientation</span> <span class="o">=</span> <span class="o">.</span><span class="n">portrait</span> <span class="n">previewView</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="nf">addSublayer</span><span class="p">(</span><span class="n">videoPreviewLayer</span><span class="p">)</span> <span class="c1">//Step12</span> <span class="p">}</span> |
We need to call -startRunning
on the session to start the live view. However, -startRunning
is a blocking method which means it will block the UI if it’s running on the main thread. If the session takes a while to start, users would want the UI to be responsive and cancel out of the camera view.
1 2 3 4 |
<span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">global</span><span class="p">(</span><span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">userInitiated</span><span class="p">)</span><span class="o">.</span><span class="n">async</span> <span class="p">{</span> <span class="c1">//[weak self] in</span> <span class="k">self</span><span class="o">.</span><span class="n">captureSession</span><span class="o">.</span><span class="nf">startRunning</span><span class="p">()</span> <span class="c1">//Step 13</span> <span class="p">}</span> |
Once the live view starts let’s set the Preview layer to fit, but we must return to the main thread to do so!
1 2 3 |
<span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="n">async</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">videoPreviewLayer</span><span class="o">.</span><span class="n">frame</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">previewView</span><span class="o">.</span><span class="n">bounds</span> <span class="p">}</span> |
Let’s create an IBAction of the Take photo Button
and capture a JPEG by calling our instance of AVCapturePhotoOutput
or stillImageOut
the method func capturePhoto(with:, delegate:)
or -capturePhotoWithSettings:delegate:
. This method requires us to provide it with a setting and a delegate to deliver the captured photo. This delegate will be this ViewController so we also need to conform to the protocol AVCapturePhotoCaptureDelegate
1 2 3 4 5 6 7 8 |
<span class="kd">class</span> <span class="kt">CameraViewControllerSwift</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">AVCapturePhotoCaptureDelegate</span> <span class="p">{</span> <span class="o">....</span> <span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">didTakePhoto</span><span class="p">(</span><span class="n">_</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">Any</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">settings</span> <span class="o">=</span> <span class="kt">AVCapturePhotoSettings</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="p">[</span><span class="kt">AVVideoCodecKey</span><span class="p">:</span> <span class="kt">AVVideoCodecType</span><span class="o">.</span><span class="n">jpeg</span><span class="p">])</span> <span class="n">stillImageOutput</span><span class="o">.</span><span class="nf">capturePhoto</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">settings</span><span class="p">,</span> <span class="nv">delegate</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> |
The AVCapturePhotoOutput
will deliver the captured photo to the assigned delegate which is our current ViewController by a delegate method called photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)
. The photo is delivered to us as an AVCapturePhoto
which is easy to transform into Data/NSData
and then into UIImage.
1 2 3 4 5 6 7 8 |
<span class="kd">func</span> <span class="nf">photoOutput</span><span class="p">(</span><span class="n">_</span> <span class="nv">output</span><span class="p">:</span> <span class="kt">AVCapturePhotoOutput</span><span class="p">,</span> <span class="n">didFinishProcessingPhoto</span> <span class="nv">photo</span><span class="p">:</span> <span class="kt">AVCapturePhoto</span><span class="p">,</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">?)</span> <span class="p">{</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">imageData</span> <span class="o">=</span> <span class="n">photo</span><span class="o">.</span><span class="nf">fileDataRepresentation</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">image</span> <span class="o">=</span> <span class="kt">UIImage</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="n">imageData</span><span class="p">)</span> <span class="n">captureImageView</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span> <span class="p">}</span> |
Let’s not forget to stop the session when we leave the camera view!
1 2 3 4 |
<span class="k">override</span> <span class="kd">func</span> <span class="nf">viewWillDisappear</span><span class="p">(</span><span class="n">_</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewWillDisappear</span><span class="p">(</span><span class="n">animated</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">captureSession</span><span class="o">.</span><span class="nf">stopRunning</span><span class="p">()</span> <span class="p">}</span> |
NOTE: The simulator does NOT have a camera so you need to run your app on an Actual Device to see the magic!
You can also know more from here!!!
Thanks for Reading!!!
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
2 comments