iOS/Swift实现自动对焦后自动拍照的方法
iOS自动对焦并自动拍照入门指南
嘿,作为iOS开发新手,这个自动对焦+自动拍照的需求其实没你想的那么复杂,核心是用AVFoundation框架来搞定——这是iOS原生处理相机、音频等媒体的底层框架,也是实现这类功能的标准方案。我给你拆解成几个关键步骤,再附上可运行的简化代码,帮你快速上手~
1. 先搞定相机权限(必做!)
iOS对隐私权限管控很严,访问相机必须提前申请权限:
- 打开项目的
Info.plist,添加NSCameraUsageDescription键,值填一句用户能看懂的说明,比如"需要访问相机来自动拍摄照片"。 - 代码里要主动请求权限,只有用户同意后才能继续配置相机:
AVCaptureDevice.requestAccess(for: .video) { granted in guard granted else { // 这里可以弹提示告诉用户去设置里开启权限 return } // 权限通过,主线程配置相机 DispatchQueue.main.async { self.setupCameraSession() } }
2. 配置相机核心会话
相机功能的核心是AVCaptureSession,它负责协调输入(摄像头)和输出(照片/视频):
- 初始化会话并设置预设:
captureSession.sessionPreset = .photo(适合拍照场景)。 - 获取后置摄像头:用
AVCaptureDevice.DiscoverySession筛选出后置的广角相机。 - 创建输入输出对象:把摄像头作为输入加入会话,把
AVCapturePhotoOutput作为照片输出加入会话。 - 添加预览层:让用户能看到相机画面,把
AVCaptureVideoPreviewLayer加到你的视图上。
3. 实现自动对焦+监听对焦完成
这是实现自动拍照的关键——我们需要让相机自动对焦,并且在对焦完成的瞬间触发拍照:
- 配置自动对焦:锁定摄像头配置,设置
focusMode = .autoFocus,对焦点设为画面中心(CGPoint(x:0.5, y:0.5)),然后解锁配置。 - 监听对焦状态:用
NSKeyValueObservation监听摄像头的adjustingFocus属性,当这个属性从true变为false时,就说明对焦完成了。
4. 对焦完成自动触发拍照
监听到对焦完成的信号后,调用AVCapturePhotoOutput的capturePhoto(with:delegate:)方法即可自动拍照,同时要实现AVCapturePhotoCaptureDelegate协议来处理拍摄完成的照片。
完整简化代码示例
下面是一个ViewController的核心代码,你可以直接放到项目里测试(记得用真机!模拟器没有相机功能):
import UIKit import AVFoundation class AutoCaptureCameraVC: UIViewController, AVCapturePhotoCaptureDelegate { private let captureSession = AVCaptureSession() private var photoOutput = AVCapturePhotoOutput() private var focusObservation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black requestCameraPermission() } private func requestCameraPermission() { AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in guard granted else { DispatchQueue.main.async { let alert = UIAlertController(title: "权限提示", message: "请在设置中开启相机权限", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "知道了", style: .default)) self?.present(alert, animated: true) } return } DispatchQueue.main.async { self?.setupCamera() } } } private func setupCamera() { captureSession.sessionPreset = .photo // 获取后置摄像头 guard let cameraDevice = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back ).devices.first else { print("找不到后置摄像头") return } do { // 添加相机输入 let cameraInput = try AVCaptureDeviceInput(device: cameraDevice) if captureSession.canAddInput(cameraInput) { captureSession.addInput(cameraInput) } // 添加照片输出 if captureSession.canAddOutput(photoOutput) { captureSession.addOutput(photoOutput) } // 添加预览层 let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = view.bounds previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) // 配置自动对焦 try cameraDevice.lockForConfiguration() cameraDevice.focusMode = .autoFocus cameraDevice.focusPointOfInterest = CGPoint(x: 0.5, y: 0.5) cameraDevice.unlockForConfiguration() // 监听对焦完成 focusObservation = cameraDevice.observe(\.adjustingFocus) { [weak self] device, _ in guard !device.adjustingFocus else { return } // 对焦完成,触发拍照 self?.takeAutoPhoto() // 如果只需要拍一次,就移除监听避免重复触发 self?.focusObservation?.invalidate() } // 启动相机会话 captureSession.startRunning() } catch { print("相机配置出错:\(error.localizedDescription)") } } private func takeAutoPhoto() { let photoSettings = AVCapturePhotoSettings() // 设置预览格式(可选,用于实时预览) if let previewFormat = photoSettings.availablePreviewPhotoPixelFormatTypes.first { photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: previewFormat] } photoOutput.capturePhoto(with: photoSettings, delegate: self) } // 处理拍摄完成的照片 func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { guard let imageData = photo.fileDataRepresentation(), let capturedImage = UIImage(data: imageData) else { print("照片处理失败:\(error?.localizedDescription ?? "未知错误")") return } // 这里可以把照片保存到相册,或者展示在界面上 let imageView = UIImageView(image: capturedImage) imageView.frame = view.bounds imageView.contentMode = .scaleAspectFit view.addSubview(imageView) // 停止相机会话(可选,根据需求决定是否继续使用相机) captureSession.stopRunning() } }
几个关键注意点
- 必须用真机测试:模拟器没有硬件相机,无法运行相机相关代码。
- 权限一定要加:如果
Info.plist里没加NSCameraUsageDescription,App会直接崩溃。 - 线程安全:相机会话的配置和启动要在主线程或者专门的串行队列里处理,避免线程冲突。
- 重复拍摄的调整:如果需要重复自动对焦拍照,不要调用
focusObservation?.invalidate(),但要注意添加防抖逻辑,避免频繁触发拍照。
内容的提问来源于stack exchange,提问作者Kévin Esprit




