Preview
Camera previews let you embed a real-time view of the camera stream in your UI.
Make sure you declare
CameraFeature.PreviewCameraFeature.previewduring 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.Displayowning the preview view. Very important in multi-display scenarios.screen: theUIScreenowning 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
}
)
