如何在iOS扫码组件中实现自动对焦和曝光?
在 iOS 扫码组件中实现自动对焦和曝光,核心依赖
AVFoundation框架的AVCaptureDevice类,通过配置设备的对焦模式、曝光模式,结合用户交互(如点击对焦)或环境检测(如弱光自动补光)来实现智能调控。以下是分步实现方案,包含基础自动对焦 / 曝光、手动触发优化及特殊场景适配:一、基础配置:开启自动对焦与曝光
首先需初始化
AVCaptureSession并获取摄像头设备(通常为后置摄像头),然后配置对焦和曝光的默认模式:swift
import AVFoundation
import UIKit
class ScanViewController: UIViewController {
private let captureSession = AVCaptureSession()
private var captureDevice: AVCaptureDevice?
private let previewLayer = AVCaptureVideoPreviewLayer()
override func viewDidLoad() {
super.viewDidLoad()
setupCaptureSession()
}
// 初始化采集会话
private func setupCaptureSession() {
// 1. 获取后置摄像头设备
guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
print("获取摄像头设备失败")
return
}
captureDevice = device
// 2. 配置输入(摄像头)
guard let input = try? AVCaptureDeviceInput(device: device) else {
print("配置摄像头输入失败")
return
}
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
// 3. 配置输出(元数据,用于识别条码)
let metadataOutput = AVCaptureMetadataOutput()
if captureSession.canAddOutput(metadataOutput) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr, .code128] // 指定识别码制
}
// 4. 配置预览层
previewLayer.session = captureSession
previewLayer.frame = view.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.insertSublayer(previewLayer, at: 0)
// 5. 开启自动对焦和曝光
setupAutoFocusAndExposure()
// 启动采集会话
captureSession.startRunning()
}
// 配置自动对焦与曝光
private func setupAutoFocusAndExposure() {
guard let device = captureDevice else { return }
do {
try device.lockForConfiguration() // 锁定配置(必须)
// 自动对焦:持续自动对焦(适合扫码时随条码移动调整)
if device.isFocusModeSupported(.continuousAutoFocus) {
device.focusMode = .continuousAutoFocus
device.focusPointOfInterest = CGPoint(x: 0.5, y: 0.5) // 对焦中心区域
}
// 自动曝光:持续自动曝光(根据环境光线调整)
if device.isExposureModeSupported(.continuousAutoExposure) {
device.exposureMode = .continuousAutoExposure
device.exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5) // 曝光中心区域
}
device.unlockForConfiguration() // 解锁配置
} catch {
print("配置对焦/曝光失败:\(error)")
}
}
}
// 实现元数据代理(扫码识别逻辑)
extension ScanViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
// 处理识别结果...
}
}
二、进阶优化:点击屏幕手动对焦 + 曝光
扫码时用户可能需要对准特定区域(如远距离条码),添加点击预览层触发对焦 / 曝光:
swift
// 在viewDidLoad中添加点击手势
override func viewDidLoad() {
super.viewDidLoad()
setupCaptureSession()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
view.addGestureRecognizer(tapGesture)
}
// 处理点击对焦
@objc private func handleTap(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: view)
// 将点击坐标转换为摄像头的兴趣区域坐标(摄像头坐标系:0~1,原点在左上角)
let cameraPoint = previewLayer.captureDevicePointConverted(fromLayerPoint: point)
focus(with: cameraPoint)
}
// 手动对焦+曝光
private func focus(with point: CGPoint) {
guard let device = captureDevice else { return }
do {
try device.lockForConfiguration()
// 1. 手动对焦到点击位置
if device.isFocusModeSupported(.autoFocus) {
device.focusPointOfInterest = point
device.focusMode = .autoFocus // 单次自动对焦(对焦后恢复持续自动对焦)
// 对焦完成后恢复持续对焦(可选)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if device.isFocusModeSupported(.continuousAutoFocus) {
device.focusMode = .continuousAutoFocus
}
}
}
// 2. 手动曝光到点击位置
if device.isExposureModeSupported(.autoExpose) {
device.exposurePointOfInterest = point
device.exposureMode = .autoExpose // 单次自动曝光
// 曝光完成后恢复持续曝光(可选)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if device.isExposureModeSupported(.continuousAutoExposure) {
device.exposureMode = .continuousAutoExposure
}
}
}
device.unlockForConfiguration()
} catch {
print("手动对焦/曝光失败:\(error)")
}
}
三、弱光场景:自定义曝光参数(ISO / 快门)
若环境光线过暗,可手动调整曝光参数(如提高 ISO、延长快门时间),实现弱光补光:
swift
// 弱光下增强曝光
private func enhanceLowLightExposure() {
guard let device = captureDevice else { return }
do {
try device.lockForConfiguration()
// 检查是否支持自定义曝光
if device.isExposureModeSupported(.custom) {
// 设置曝光时长(单位:秒,需在device.minExposureDuration和maxExposureDuration之间)
let exposureDuration = CMTimeMakeWithSeconds(1/30, preferredTimescale: 1000000000) // 30fps
// 设置ISO(需在device.minISO和maxISO之间)
let iso = min(400, device.maxISO) // 提高ISO至400(避免噪点过多)
device.setExposureModeCustom(duration: exposureDuration, iso: iso, completionHandler: nil)
}
device.unlockForConfiguration()
} catch {
print("自定义曝光失败:\(error)")
}
}
四、闪光灯联动:弱光自动开启闪光灯
结合环境光检测,在弱光时自动打开闪光灯辅助照明:
swift
// 自动控制闪光灯
private func autoToggleTorch() {
guard let device = captureDevice, device.hasTorch else { return }
do {
try device.lockForConfiguration()
// 模拟环境光检测(实际可通过亮度传感器获取)
let isLowLight = true // 弱光标记
device.torchMode = isLowLight ? .on : .off
// 设置闪光灯亮度(0~1)
if device.isTorchModeSupported(.on) {
try device.setTorchModeOn(level: 0.8)
}
device.unlockForConfiguration()
} catch {
print("闪光灯控制失败:\(error)")
}
}
五、关键注意事项
- 配置锁定机制:所有对
AVCaptureDevice的配置修改必须在lockForConfiguration()和unlockForConfiguration()之间进行,否则会崩溃。 - 设备能力检查:不同设备支持的对焦 / 曝光模式不同(如部分旧机型不支持自定义曝光),需通过
isFocusModeSupported()、isExposureModeSupported()判断。 - 坐标系转换:点击屏幕的坐标是
UIKit坐标系(原点在左上角),需通过previewLayer.captureDevicePointConverted(fromLayerPoint:)转换为摄像头的兴趣区域坐标(0~1 范围)。 - 性能平衡:持续自动对焦 / 曝光会增加 CPU 消耗,若需极致性能,可在识别到条码后暂停自动调整。
总结
通过
AVFoundation的AVCaptureDevice配置,可实现基础自动对焦 / 曝光、点击手动调控及弱光场景优化,结合闪光灯联动能进一步提升扫码成功率。实际开发中需根据业务场景(如支付扫码、商品盘点)调整策略,例如支付场景优先保证识别速度,商品盘点场景支持连续对焦适配批量扫码。

浙公网安备 33010602011771号