Other versions of this page
Preview
Camera previews let you embed a real-time view of the camera stream in your UI.
Make sure you declare
CameraFeature.Preview
CameraFeature.preview
during manager initialization.
Surface management
Creating surfaces
Surfaces are declared by using CameraManager.createSurface
and CameraManager.destroySurface
. These APIs are designed
to be framework-agnostic so they can work, for instance, with both android.view.View
and Jetpack
Composeboth UIKit and SwiftUI.
Instead of a View
, the API requires a android.view.Surface
:
kotlin// CameraManager API
fun createSurface(
name: String,
target: Surface,
display: Int?,
preview: Boolean = true,
video: Boolean = false,
)
Instead of a view or view controller, the API requires a CAMetalLayer
:
swift// CameraManager API
func createSurface(
_ name: String,
target: CAMetalLayer,
screen: UIScreen?,
preview: Bool = true,
video: Bool = false
)
Let's go through the arguments and their meaning:
name
: an user-defined identifier for this surface. Used later during destroytarget
: will be forwarded image frames from the camera stream-
display
: the ID of theandroid.view.Display
owning the preview view. Very important in multi-display scenarios.screen
: theUIScreen
owning the preview view. - (optional)
preview
: whether this surface should receive preview frames. Defaults to true. - (optional)
video
: whether this surface should receive video recording frames. Defaults to false.
Keep reading below to see a real-word example.
Destroying surfaces
For proper resource management, it is fundamental that you also destroy the surface once the owner view is disposed:
kotlin// CameraManager API
fun destroySurface(name: String)
swift// CameraManager API
func destroySurface(_ name: String)
UI Integration
The code below shows one of the possible ways you can integrate previews in your KotlinSwift application. Note that Multiple previews for the same camera are allowed: just make sure to use a different surface name.
For UIKit-based apps, you may use the following view implementation:
swiftclass CameraPreview : UIView {
let manager: CameraManager
let name: String
init(manager: CameraManager, name: String = "DefaultCameraPreview") {
self.manager = manager
self.name = name
super.init(frame: .zero)
backgroundColor = .clear
}
required init?(coder: NSCoder) {
return nil
}
override class var layerClass: AnyClass { CAMetalLayer.self }
override var layer: CAMetalLayer { super.layer as! CAMetalLayer }
override func willMove(toWindow newWindow: UIWindow?) {
if newWindow == nil { manager.destroySurface(name) }
super.willMove(toWindow: newWindow)
}
override func didMoveToWindow() {
super.didMoveToWindow()
if let window {
layer.contentsScale = window.screen.scale
resizeLayer()
manager.createSurface(name, target: layer, screen: window.screen)
}
}
override func layoutSubviews() {
super.layoutSubviews()
resizeLayer()
}
private func resizeLayer() {
layer.drawableSize = .init(width: layer.frame.width * layer.contentsScale, height: layer.frame.height * layer.contentsScale)
}
}
For SwiftUI, you can simply wrap the view above in a UIViewRepresentable
.
For android.view.*
-based apps, you may use a SurfaceView
:
kotlinclass SurfaceHolderCallback(
private val surfaceView: SurfaceView,
private val manager: CameraManager,
private val name: String = "DefaultCameraPreview"
) : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
manager.createSurface(
name = name,
target = holder.surface,
display = surfaceView.display.displayId
)
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
// Nothing to do
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
manager.destroySurface(name)
}
}
val surfaceView = SurfaceView(context) // or findViewById()
surfaceView.holder.addCallback(SurfaceHolderCallback(surfaceView, manager))
// Don't forget to add the SurfaceView to your view hierarchy, if it's not there already.
For Jetpack Compose apps, simply wrap the SurfaceView
above in an AndroidView
:
kotlinAndroidView(
modifier = modifier,
factory = {
val surfaceView = SurfaceView(it)
surfaceView.holder.addCallback(SurfaceHolderCallback(surfaceView, manager))
surfaceView
}
)