如何像裁剪图片一样裁剪AVCaptureVideoPreviewLayer视频预览层?
实现AVCaptureVideoPreviewLayer的圆形裁剪+描边效果
当然可以搞定!我之前也碰到过类似的需求,AVCaptureVideoPreviewLayer作为CALayer的子类,我们可以用Core Animation的特性来实现和SwiftUI里clipShape(Circle())加描边几乎一样的效果,具体步骤如下:
1. 给预览层添加圆形遮罩实现裁剪
AVCaptureVideoPreviewLayer本身支持通过mask属性设置裁剪形状,我们可以用CAShapeLayer创建一个圆形路径作为遮罩:
// 先创建你的previewLayer(保持你原有的代码) let captureSession = AVCaptureSession() // ... 这里省略session的配置代码 ... let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = CGRect(x: 20, y: 60, width: 335, height: 200) previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) // 创建圆形遮罩层 let circleMask = CAShapeLayer() circleMask.path = UIBezierPath(ovalIn: previewLayer.bounds).cgPath // 把遮罩赋值给previewLayer,实现圆形裁剪 previewLayer.mask = circleMask
2. 添加绿色描边的圆形图层
因为遮罩会把超出圆形范围的内容全部裁剪掉,所以不能直接给previewLayer加描边(加了也看不到),需要单独创建一个描边图层,放在previewLayer的上方或者同级位置:
// 创建描边用的形状层 let strokeLayer = CAShapeLayer() // 复用之前的圆形路径,保证和裁剪形状一致 strokeLayer.path = circleMask.path // 设置描边样式 strokeLayer.strokeColor = UIColor.green.cgColor strokeLayer.fillColor = UIColor.clear.cgColor // 填充色设为透明,只显示描边 strokeLayer.lineWidth = 1.0 // 把描边图层添加到父视图的layer上,位置和previewLayer保持一致 view.layer.addSublayer(strokeLayer) strokeLayer.frame = previewLayer.frame
3. 适配视图布局变化
如果你的视图会发生布局变化(比如屏幕旋转、横竖屏切换),需要在布局更新时同步更新遮罩和描边的路径,避免形状错位:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 找到之前创建的遮罩层和描边层 guard let maskLayer = previewLayer.mask as? CAShapeLayer, let strokeLayer = view.layer.sublayers?.first(where: { guard let shapeLayer = $0 as? CAShapeLayer else { return false } return shapeLayer.strokeColor == UIColor.green.cgColor }) as? CAShapeLayer else { return } // 更新路径和frame maskLayer.path = UIBezierPath(ovalIn: previewLayer.bounds).cgPath strokeLayer.path = maskLayer.path strokeLayer.frame = previewLayer.frame }
扩展:实现其他形状的裁剪
如果想要换成圆角矩形或者自定义形状,只需要修改UIBezierPath的创建方式即可,比如圆角矩形:
let roundedRectPath = UIBezierPath(roundedRect: previewLayer.bounds, cornerRadius: 20) circleMask.path = roundedRectPath.cgPath strokeLayer.path = roundedRectPath.cgPath
这样就能实现和你给出的SwiftUI示例几乎完全一致的视觉效果啦!
内容的提问来源于stack exchange,提问作者triiiiista
相关产品推荐
相关产品推荐

