Camera integration enables users to capture photos and videos directly in your app. Learn to implement camera features and manage photo libraries.
Camera Access
Request Camera Permission
swift
import AVFoundation
import Photos
class CameraPermissionManager: ObservableObject {
@Published var cameraPermissionStatus: AVAuthorizationStatus = .notDetermined
@Published var photoLibraryPermissionStatus: PHAuthorizationStatus = .notDetermined
func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { granted in
DispatchQueue.main.async {
self.cameraPermissionStatus = granted ? .authorized : .denied
}
}
}
func requestPhotoLibraryPermission() {
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
self.photoLibraryPermissionStatus = status
}
}
}
}UIImagePickerController
Basic Camera Integration
swift
import UIKit
import SwiftUI
struct CameraPicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
@Binding var isPresented: Bool
var sourceType: UIImagePickerController.SourceType = .camera
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(image: $image, isPresented: $isPresented)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
@Binding var image: UIImage?
@Binding var isPresented: Bool
init(image: Binding<UIImage?>, isPresented: Binding<Bool>) {
self._image = image
self._isPresented = isPresented
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let uiImage = info[.originalImage] as? UIImage {
image = uiImage
}
isPresented = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isPresented = false
}
}
}SwiftUI Camera View
swift
struct CameraView: View {
@State private var selectedImage: UIImage?
@State private var showImagePicker = false
@State private var showCamera = false
var body: some View {
VStack {
if let image = selectedImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(height: 300)
.cornerRadius(10)
}
HStack(spacing: 20) {
Button("Camera") {
showCamera = true
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
Button("Photo Library") {
showImagePicker = true
}
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.sheet(isPresented: $showCamera) {
CameraPicker(
image: $selectedImage,
isPresented: $showCamera,
sourceType: .camera
)
}
.sheet(isPresented: $showImagePicker) {
CameraPicker(
image: $selectedImage,
isPresented: $showImagePicker,
sourceType: .photoLibrary
)
}
}
}Custom Camera UI
AVFoundation Camera
swift
import AVFoundation
import SwiftUI
struct CustomCameraView: UIViewRepresentable {
class CameraCoordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
var captureSession: AVCaptureSession?
var previewLayer: AVCaptureVideoPreviewLayer?
var photoOutput: AVCapturePhotoOutput?
func setupCamera() {
captureSession = AVCaptureSession()
guard let captureSession = captureSession else { return }
captureSession.sessionPreset = .photo
guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
print("Unable to access back camera")
return
}
do {
let input = try AVCaptureDeviceInput(device: backCamera)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
photoOutput = AVCapturePhotoOutput()
if let photoOutput = photoOutput,
captureSession.canAddOutput(photoOutput) {
captureSession.addOutput(photoOutput)
}
DispatchQueue.global(qos: .userInitiated).async {
captureSession.startRunning()
}
} catch {
print("Error setting up camera: \(error)")
}
}
func capturePhoto() {
guard let photoOutput = photoOutput else { return }
let settings = AVCapturePhotoSettings()
photoOutput.capturePhoto(with: settings, delegate: self)
}
}
func makeUIView(context: Context) -> UIView {
let view = UIView()
context.coordinator.setupCamera()
if let previewLayer = context.coordinator.previewLayer {
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
func makeCoordinator() -> CameraCoordinator {
CameraCoordinator()
}
}Photo Library
Fetch Photos
swift
import Photos
class PhotoLibraryManager: ObservableObject {
@Published var photos: [PHAsset] = []
func fetchPhotos() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.fetchLimit = 50
let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: .image, options: fetchOptions)
DispatchQueue.main.async {
self.photos = assets.objects(at: IndexSet(integersIn: 0..<assets.count))
}
}
func requestImage(for asset: PHAsset, size: CGSize, completion: @escaping (UIImage?) -> Void) {
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
let targetSize = CGSize(width: size.width * UIScreen.main.scale, height: size.height * UIScreen.main.scale)
PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, _ in
completion(image)
}
}
}Save Photos
swift
extension PhotoLibraryManager {
func saveImage(_ image: UIImage) async throws {
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
}
}
func saveImageWithMetadata(_ image: UIImage, location: CLLocation?) async throws {
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
if let location = location {
request.location = location
}
}
}
}Image Processing
Filter Effects
swift
import CoreImage
import CoreImage.CIFilterBuiltins
class ImageProcessor {
func applyFilter(to image: UIImage, filterName: String) -> UIImage? {
guard let ciImage = CIImage(image: image) else { return nil }
let context = CIContext()
if let filter = CIFilter(name: filterName) {
filter.setValue(ciImage, forKey: kCIInputImageKey)
if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage)
}
}
return nil
}
func applySepia(to image: UIImage) -> UIImage? {
let filter = SepiaTone()
filter.inputImage = CIImage(image: image)
filter.intensity = 0.8
let context = CIContext()
if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage)
}
return nil
}
func adjustBrightness(image: UIImage, value: Double) -> UIImage? {
let filter = CIFilter.exposureAdjust()
filter.inputImage = CIImage(image: image)
filter.ev = value
let context = CIContext()
if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage)
}
return nil
}
}Video Recording
Record Video
swift
class VideoRecorder: ObservableObject {
private let captureSession = AVCaptureSession()
private var videoOutput: AVCaptureMovieFileOutput?
private var outputFileURL: URL?
func setupRecording() {
captureSession.sessionPreset = .high
guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
do {
let input = try AVCaptureDeviceInput(device: backCamera)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
videoOutput = AVCaptureMovieFileOutput()
if let videoOutput = videoOutput,
captureSession.canAddOutput(videoOutput) {
captureSession.addOutput(videoOutput)
}
captureSession.startRunning()
} catch {
print("Error setting up video: \(error)")
}
}
func startRecording() {
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
outputFileURL = documentsPath.appendingPathComponent("video.mp4")
if let videoOutput = videoOutput, let url = outputFileURL {
videoOutput.startRecording(to: url, recordingDelegate: self)
}
}
func stopRecording() {
videoOutput?.stopRecording()
}
}
extension VideoRecorder: AVCaptureFileOutputRecordingDelegate {
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if let error = error {
print("Recording error: \(error)")
return
}
print("Video saved to: \(outputFileURL)")
}
}Best Practices
- 1Permissions: Request permissions appropriately
- 2Memory: Manage memory carefully with photos
- 3Performance: Optimize image loading
- 4Privacy: Respect user privacy
- 5Storage: Handle storage limitations
- 6Compression: Compress images for storage
- 7Metadata: Preserve photo metadata
- 8Testing: Test on real devices
Camera and photo integration enhances user experience!