Swift中如何在AVCaptureVideoPreviewLayer上添加矩形图层?
解决AVCaptureVideoPreviewLayer上添加矩形图层不显示的问题
我帮你排查了代码里的几个关键问题,这就是你的矩形图层不生效的原因:
1. 变量作用域错误(最核心问题)
你全局声明了var previewLayer = AVCaptureVideoPreviewLayer.init(),但在viewDidLoad里又重新创建了一个局部的previewLayer变量:
// 这里的let previewLayer是局部变量,和全局的var previewLayer完全不是同一个! let previewLayer = AVCaptureVideoPreviewLayer.init(session: captureSession)
这导致全局的previewLayer始终是初始化时的空图层,触发事件里调用self.previewLayer.addSublayer(rec)根本没加到实际显示的预览层上。
2. 矩形坐标与预览层适配问题
AVCaptureVideoPreviewLayer的videoGravity(默认是.resizeAspect)会让相机画面按比例缩放,直接用view的坐标可能和预览层的实际可视区域不匹配,导致矩形显示在不可见区域。
修正后的完整代码
第一步:修复previewLayer的赋值逻辑
把viewDidLoad里的局部变量改成给全局变量赋值,确保后续操作的是同一个图层:
var previewLayer = AVCaptureVideoPreviewLayer() var previewFrame = CGRect() override func viewDidLoad() { super.viewDidLoad() let captureSession = AVCaptureSession() captureSession.beginConfiguration() // 配置相机输入 guard let videoDevice = AVCaptureDevice.default(for: .video), let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice), captureSession.canAddInput(videoDeviceInput) else { return } captureSession.addInput(videoDeviceInput) // 配置视频输出 let videoOutput = AVCaptureVideoDataOutput() guard captureSession.canAddOutput(videoOutput) else { return } videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue")) captureSession.addOutput(videoOutput) captureSession.commitConfiguration() captureSession.startRunning() // 给全局previewLayer赋值,而非创建局部变量 previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) view.layer.addSublayer(previewLayer) previewLayer.frame = view.bounds // 用bounds比frame更适配view的布局 previewFrame = previewLayer.bounds }
第二步:修复触发事件的矩形绘制代码
这里增加了坐标转换逻辑,确保矩形在预览层的可视区域正确显示:
// 触发矩形绘制的示例方法 func drawRectangleOnPreview() { // 先把基于view坐标系的点转换到previewLayer的坐标系 let point1 = previewLayer.convert(CGPoint(x: value1, y: value2), from: view) let point2 = previewLayer.convert(CGPoint(x: value3, y: value4), from: view) let point3 = previewLayer.convert(CGPoint(x: value5, y: value6), from: view) let point4 = previewLayer.convert(CGPoint(x: value7, y: value8), from: view) let rectangle = UIBezierPath() rectangle.move(to: point1) rectangle.addLine(to: point2) rectangle.addLine(to: point3) rectangle.addLine(to: point4) rectangle.close() let rec = CAShapeLayer() rec.path = rectangle.cgPath rec.fillColor = UIColor.orange.withAlphaComponent(0.3).cgColor // 加透明度避免完全遮挡画面 rec.strokeColor = UIColor.red.cgColor // 可选:添加边框便于识别 rec.lineWidth = 2.0 // 可选:移除之前的矩形图层,避免重复绘制导致图层堆积 previewLayer.sublayers?.filter { $0 is CAShapeLayer }.forEach { $0.removeFromSuperlayer() } previewLayer.addSublayer(rec) }
额外提示
- 如果你的矩形坐标是来自视频帧的像素分析(比如人脸检测结果),需要用
previewLayer.metadataOutputRectConverted(fromLayerRect:)方法做坐标转换,适配不同设备的屏幕比例和videoGravity设置。 - 频繁触发绘制时,记得清理旧图层,避免内存占用过高。
内容的提问来源于stack exchange,提问作者johnDoe
相关产品推荐
相关产品推荐

