在 UIView 的背景上设置摄像头
Set up camera on the background of UIView
我正在尝试在 UIViewController 的 UIView 背景上设置摄像头,以便能够在其上绘图。
怎么做?
一种简单的方法是在 imagepickercontroller 上添加叠加视图并隐藏默认视图。
另一种方法是使用 AV 框架,这将为您提供更多选择和自由。
选择取决于您的需要。
更新为 SWIFT 5
您可以尝试这样的操作:
我在 UIViewController
的主视图中添加了两个 UIView
,一个叫做 previewView
(用于相机),另一个 UIView
叫做 boxView
(在摄像机视图上方)
class ViewController: UIViewController {
var previewView : UIView!
var boxView:UIView!
//Camera Capture requiered properties
var videoDataOutput: AVCaptureVideoDataOutput!
var videoDataOutputQueue: DispatchQueue!
var previewLayer:AVCaptureVideoPreviewLayer!
var captureDevice : AVCaptureDevice!
let session = AVCaptureSession()
var currentFrame: CIImage!
var done = false
override func viewDidLoad() {
super.viewDidLoad()
previewView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
previewView.contentMode = .scaleAspectFit
view.addSubview(previewView)
//Add a box view
boxView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
boxView.backgroundColor = UIColor.green
boxView.alpha = 0.3
view.addSubview(boxView)
self.setupAVCapture()
}
override func viewWillAppear(_ animated: Bool) {
if !done {
session.startRunning()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override var shouldAutorotate: Bool {
if (UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft ||
UIDevice.current.orientation == UIDeviceOrientation.landscapeRight ||
UIDevice.current.orientation == UIDeviceOrientation.unknown) {
return false
}
else {
return true
}
}
}
// AVCaptureVideoDataOutputSampleBufferDelegate protocol and related methods
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate{
func setupAVCapture(){
session.sessionPreset = AVCaptureSession.Preset.vga640x480
guard let device = AVCaptureDevice
.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera,
for: .video,
position: AVCaptureDevice.Position.front) else{
return
}
captureDevice = device
beginSession()
done = true
}
func beginSession(){
var deviceInput: AVCaptureDeviceInput!
do {
deviceInput = try AVCaptureDeviceInput(device: captureDevice)
guard deviceInput != nil else {
print("error: cant get deviceInput")
return
}
if self.session.canAddInput(deviceInput){
self.session.addInput(deviceInput)
}
videoDataOutput = AVCaptureVideoDataOutput()
videoDataOutput.alwaysDiscardsLateVideoFrames=true
videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
videoDataOutput.setSampleBufferDelegate(self, queue:self.videoDataOutputQueue)
if session.canAddOutput(self.videoDataOutput){
session.addOutput(self.videoDataOutput)
}
videoDataOutput.connection(with: AVMediaType.video)?.isEnabled = true
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
let rootLayer: CALayer = self.previewView.layer
rootLayer.masksToBounds = true
self.previewLayer.frame = rootLayer.bounds
rootLayer.addSublayer(self.previewLayer)
session.startRunning()
} catch let error as NSError {
deviceInput = nil
print("error: \(error.localizedDescription)")
}
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
currentFrame = self.convertImageFromCMSampleBufferRef(sampleBuffer)
}
// clean up AVCapture
func stopCamera(){
session.stopRunning()
done = false
}
func convertImageFromCMSampleBufferRef(_ sampleBuffer:CMSampleBuffer) -> CIImage{
let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let ciImage:CIImage = CIImage(cvImageBuffer: pixelBuffer)
return ciImage
}
}
您可以将 boxView 的 frame
替换为 mainView 的 frame
,并且不要设置其背景 属性。这样你就可以使用这个视图来添加更多的子视图了。
IMPORTANT
Remember that in iOS 10 you need to first ask the user for permission in order to have access to the camera. You do this by adding a usage
key to your app’s Info.plist
together with a purpose string
because if you fail to declare the usage, your app will crash when it
first makes the access.
这是显示相机访问请求的屏幕截图
希望对您有所帮助!
另一方面,SceneView 对增强现实应用程序很有用。
- 用AVFramework或UIView创建一个预览层,然后添加预览
图层到视图的子图层。
- 创建并自定义场景视图。然后将场景视图添加到视图的
子视图。
创建和自定义场景。最后添加到scenview的场景中。
// 1. Create a preview layer with AVFramework or UIView, then add preview layer to view's sublayer.
self.previewLayer!.frame = view.layer.bounds
view.clipsToBounds = true
view.layer.addSublayer(self.previewLayer!)
// 2. Create and custumize a sceneview. Then add sceneview to view's subview.
let sceneView = SCNView()
sceneView.frame = view.bounds
sceneView.backgroundColor = UIColor.clearColor()
self.previewLayer!.frame = view.bounds
view.addSubview(sceneView)
// 3 . Create and custimize scene. Finally add to scenview's scene.
let scene = SCNScene()
sceneView.autoenablesDefaultLighting = true
sceneView.allowsCameraControl = true
let boxGeometry = SCNBox(width: 800 , height: 400, length: 1.0, chamferRadius: 1.0)
let yellow = UIColor.yellowColor()
let semi = yellow.colorWithAlphaComponent(0.3)
boxGeometry.firstMaterial?.diffuse.contents = semi
let boxNode = SCNNode(geometry: boxGeometry)
scene.rootNode.addChildNode(boxNode)
sceneView.scene = scene
我正在尝试在 UIViewController 的 UIView 背景上设置摄像头,以便能够在其上绘图。
怎么做?
一种简单的方法是在 imagepickercontroller 上添加叠加视图并隐藏默认视图。
另一种方法是使用 AV 框架,这将为您提供更多选择和自由。
选择取决于您的需要。
更新为 SWIFT 5
您可以尝试这样的操作:
我在 UIViewController
的主视图中添加了两个 UIView
,一个叫做 previewView
(用于相机),另一个 UIView
叫做 boxView
(在摄像机视图上方)
class ViewController: UIViewController {
var previewView : UIView!
var boxView:UIView!
//Camera Capture requiered properties
var videoDataOutput: AVCaptureVideoDataOutput!
var videoDataOutputQueue: DispatchQueue!
var previewLayer:AVCaptureVideoPreviewLayer!
var captureDevice : AVCaptureDevice!
let session = AVCaptureSession()
var currentFrame: CIImage!
var done = false
override func viewDidLoad() {
super.viewDidLoad()
previewView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
previewView.contentMode = .scaleAspectFit
view.addSubview(previewView)
//Add a box view
boxView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
boxView.backgroundColor = UIColor.green
boxView.alpha = 0.3
view.addSubview(boxView)
self.setupAVCapture()
}
override func viewWillAppear(_ animated: Bool) {
if !done {
session.startRunning()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override var shouldAutorotate: Bool {
if (UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft ||
UIDevice.current.orientation == UIDeviceOrientation.landscapeRight ||
UIDevice.current.orientation == UIDeviceOrientation.unknown) {
return false
}
else {
return true
}
}
}
// AVCaptureVideoDataOutputSampleBufferDelegate protocol and related methods
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate{
func setupAVCapture(){
session.sessionPreset = AVCaptureSession.Preset.vga640x480
guard let device = AVCaptureDevice
.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera,
for: .video,
position: AVCaptureDevice.Position.front) else{
return
}
captureDevice = device
beginSession()
done = true
}
func beginSession(){
var deviceInput: AVCaptureDeviceInput!
do {
deviceInput = try AVCaptureDeviceInput(device: captureDevice)
guard deviceInput != nil else {
print("error: cant get deviceInput")
return
}
if self.session.canAddInput(deviceInput){
self.session.addInput(deviceInput)
}
videoDataOutput = AVCaptureVideoDataOutput()
videoDataOutput.alwaysDiscardsLateVideoFrames=true
videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
videoDataOutput.setSampleBufferDelegate(self, queue:self.videoDataOutputQueue)
if session.canAddOutput(self.videoDataOutput){
session.addOutput(self.videoDataOutput)
}
videoDataOutput.connection(with: AVMediaType.video)?.isEnabled = true
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
let rootLayer: CALayer = self.previewView.layer
rootLayer.masksToBounds = true
self.previewLayer.frame = rootLayer.bounds
rootLayer.addSublayer(self.previewLayer)
session.startRunning()
} catch let error as NSError {
deviceInput = nil
print("error: \(error.localizedDescription)")
}
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
currentFrame = self.convertImageFromCMSampleBufferRef(sampleBuffer)
}
// clean up AVCapture
func stopCamera(){
session.stopRunning()
done = false
}
func convertImageFromCMSampleBufferRef(_ sampleBuffer:CMSampleBuffer) -> CIImage{
let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let ciImage:CIImage = CIImage(cvImageBuffer: pixelBuffer)
return ciImage
}
}
您可以将 boxView 的 frame
替换为 mainView 的 frame
,并且不要设置其背景 属性。这样你就可以使用这个视图来添加更多的子视图了。
IMPORTANT
Remember that in iOS 10 you need to first ask the user for permission in order to have access to the camera. You do this by adding a usage key to your app’s
Info.plist
together with a purpose string because if you fail to declare the usage, your app will crash when it first makes the access.
这是显示相机访问请求的屏幕截图
希望对您有所帮助!
另一方面,SceneView 对增强现实应用程序很有用。
- 用AVFramework或UIView创建一个预览层,然后添加预览 图层到视图的子图层。
- 创建并自定义场景视图。然后将场景视图添加到视图的 子视图。
创建和自定义场景。最后添加到scenview的场景中。
// 1. Create a preview layer with AVFramework or UIView, then add preview layer to view's sublayer. self.previewLayer!.frame = view.layer.bounds view.clipsToBounds = true view.layer.addSublayer(self.previewLayer!) // 2. Create and custumize a sceneview. Then add sceneview to view's subview. let sceneView = SCNView() sceneView.frame = view.bounds sceneView.backgroundColor = UIColor.clearColor() self.previewLayer!.frame = view.bounds view.addSubview(sceneView) // 3 . Create and custimize scene. Finally add to scenview's scene. let scene = SCNScene() sceneView.autoenablesDefaultLighting = true sceneView.allowsCameraControl = true let boxGeometry = SCNBox(width: 800 , height: 400, length: 1.0, chamferRadius: 1.0) let yellow = UIColor.yellowColor() let semi = yellow.colorWithAlphaComponent(0.3) boxGeometry.firstMaterial?.diffuse.contents = semi let boxNode = SCNNode(geometry: boxGeometry) scene.rootNode.addChildNode(boxNode) sceneView.scene = scene