Custom Camera UI With AVFoundation in SwiftUI
A custom camera is a live preview layer plus a capture session. Reach for it only when the system picker will not do, and always ask permission honestly.
TL;DR
A custom camera UI in SwiftUI wraps an AVFoundation capture session: a full-screen live preview, a shutter, and controls for flash, front and back, and focus. Build the controls from a free VP0 design and bridge the preview layer into SwiftUI. Two rules: add the camera usage description to Info.plist or iOS blocks the camera, and reach for a custom camera only when you need control the system picker cannot give, since the built-in picker is simpler and more private for one-off captures.
Want a custom camera screen in SwiftUI rather than the plain system picker? The short answer: wrap an AVFoundation capture session, show its live preview full-screen, and build your own shutter and controls. But first ask whether you actually need it, because for a one-off photo the system picker is simpler and more private. When you do need control, build the controls from a free VP0 design, the free iOS design library for AI builders, and handle the camera permission honestly.
Who this is for
This is for builders who need a custom capture experience, an overlay, a guided scan, specific settings, or a branded flow, and want to wire AVFoundation into SwiftUI correctly with the right permissions.
How a custom camera works
The engine is an AVCaptureSession: it connects a camera input to a photo or video output and runs the live feed. You show that feed by bridging the preview layer into SwiftUI, then layer your own UI on top, a shutter, a flash toggle, a front and back switch, tap-to-focus, as Apple’s capture setup docs describe. The non-negotiable: iOS will not start the camera without a usage string, so add NSCameraUsageDescription to Info.plist, request permission, and handle a refusal without crashing.
| Piece | Tool | Get it right |
|---|---|---|
| Live preview | AVCaptureSession preview | Full-screen, correct orientation |
| Shutter | Capture photo output | Clear feedback on capture |
| Flash and lens | Session configuration | Toggle front, back, flash |
| Focus | Tap to focus | Expose at the tapped point |
| Permission | Camera usage string | Request, handle denied |
Build it free with a VP0 design
Pick a camera or capture design from VP0, copy its link, and prompt your AI builder:
Rebuild this VP0 camera design in SwiftUI: [paste VP0 link]. Wrap an AVCaptureSession with a full-screen preview bridged into SwiftUI, and build a shutter, flash toggle, front and back switch, and tap-to-focus. Add the camera usage description, request permission with a clear purpose, and handle the denied state. Keep the overlay simple.
Cameras are central to mobile: people take well over 1,000,000,000,000 photos a year on phones, so a great capture flow matters. For neighboring hardware patterns, see a Bluetooth device pairing UI in SwiftUI, Core NFC and Tap to Pay for AI-built apps, an AI headshot generator app source code for a photo product, and an e-commerce AR try-on app template for a camera-driven overlay. For a workforce tool that uses location, see an employee geofence clock-in UI in React Native.
Privacy first, and pick the simpler tool
Two honesty points. Privacy: the camera is sensitive, so request access with a clear, specific purpose string, only capture what you need, and never quietly record. And restraint: a custom camera is more code and more responsibility than the system image picker, so use it only when you genuinely need the control. For a simple profile photo, the picker is fewer lines, more private, and what Apple nudges you toward. Choosing the simpler tool when it fits is a feature, not a cop-out.
Common mistakes
The first mistake is forgetting the camera usage string, so the camera silently fails. The second is building a custom camera when the system picker would do. The third is not handling denied permission, so the screen is blank. The fourth is a preview with the wrong orientation or aspect. The fifth is paying for a camera kit when a free VP0 design plus AVFoundation does it.
Key takeaways
- A custom camera is an AVCaptureSession preview plus your own controls.
- Add the camera usage string or iOS blocks the camera.
- Request permission with a clear purpose and handle the denied state.
- Use a custom camera only when the system picker cannot do the job.
- Build the controls free from a VP0 design.
Frequently asked questions
How do I build a custom camera UI in SwiftUI with AVFoundation? Set up an AVCaptureSession, show its preview full-screen in SwiftUI, build a shutter and controls, add the camera usage string, and request permission, handling the denied state.
What is the safest way to build a camera screen with Claude Code or Cursor? Start from a free VP0 design, wrap an AVCaptureSession with a SwiftUI preview, add the usage string, request permission clearly, and consider whether the system picker is simpler.
Can VP0 provide a free SwiftUI or React Native template for a camera UI? Yes. VP0 is a free iOS design library; pick a camera design and your AI tool rebuilds the shutter, controls, and overlay while AVFoundation runs the session.
When should I build a custom camera instead of using the system picker? Only when you need control the picker cannot give, like an overlay or continuous scanning. For a one-off photo, the system picker is simpler and more private.
Frequently asked questions
How do I build a custom camera UI in SwiftUI with AVFoundation?
Set up an AVFoundation capture session, show its preview layer full-screen by bridging it into SwiftUI, and build the shutter and controls for flash, camera switching, and focus from your design. Add the camera usage description to Info.plist, request permission, and handle the denied state gracefully.
What is the safest way to build a camera screen with Claude Code or Cursor?
Start from a free VP0 design and prompt the tool to wrap an AVCaptureSession with a SwiftUI preview, add the camera usage string, and request permission with a clear purpose. Handle denied access, and consider whether the system image picker would be simpler and more private for your use case.
Can VP0 provide a free SwiftUI or React Native template for a camera UI?
Yes. VP0 is a free iOS design library for AI builders. Pick a camera or capture design, copy its link, and your AI tool rebuilds the shutter, controls, and overlay at no cost while AVFoundation runs the session.
When should I build a custom camera instead of using the system picker?
Use a custom camera only when you need control the picker cannot give, such as a custom overlay, continuous scanning, specific capture settings, or a branded capture flow. For a simple one-off photo, the system image picker is less code, more private, and what most apps should use.
Part of the Native Hardware, Sensors & Device Features hub. Browse all VP0 topics →
Keep reading
iOS Document Picker UI Customization in SwiftUI
Use the iOS document picker in SwiftUI to import and export files from Files and iCloud, from a free VP0 design. With security-scoped access.
Apple HealthKit Step Counter in SwiftUI (Free Template)
Build a step-counter UI on HealthKit in SwiftUI: permission, today's steps, a trend chart, and goals, from a free VP0 design. Private, and not medical.
Bluetooth Device Pairing UI in SwiftUI (Free Template)
A BLE pairing screen scans, lists nearby devices, and walks through connecting with clear states. Build it with Core Bluetooth from a free VP0 design.
Video Editor Timeline UI in iOS (Learn the CapCut Pattern)
Build a video editor timeline UI in SwiftUI: scrubbable clips, trim handles, and layered tracks, from a free VP0 design. AVFoundation does the editing.
App Tracking Transparency Prompt UI in SwiftUI
How to do the App Tracking Transparency prompt right in SwiftUI: prime it in context, ask at the correct moment, and keep the app working when the user says no.
iOS Address Autocomplete: Google Places or Free MapKit
How to build an address autocomplete UI for iOS with the Google Places API, plus MKLocalSearchCompleter, the free native alternative built into MapKit.