在 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 对增强现实应用程序很有用。

  1. 用AVFramework或UIView创建一个预览层,然后添加预览 图层到视图的子图层。
  2. 创建并自定义场景视图。然后将场景视图添加到视图的 子视图。
  3. 创建和自定义场景。最后添加到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