iOS Swift开发:如何用AVCapturePhoto嵌入照片位置数据?
解决AVCapturePhoto嵌入GPS位置数据的方法
嘿,我之前在做类似的Swift iOS项目时也碰到过这个问题!用AVCapturePhoto拍出来的照片默认确实不会自动带GPS数据,得我们手动把位置信息塞进元数据里才行。下面是我整理的完整步骤,亲测有效:
1. 先搞定位置权限(必备前提)
要获取位置数据,首先得拿到用户的授权。
首先在
Info.plist中添加以下两个键之一(根据你的需求选择):NSLocationWhenInUseUsageDescription:仅在App使用时请求位置权限NSLocationAlwaysAndWhenInUseUsageDescription:请求始终允许位置权限
键值填一段说明,告诉用户为什么需要位置权限,比如“需要为拍摄的照片添加位置标记”。
然后用
CLLocationManager请求权限:
import CoreLocation class YourCameraViewController: UIViewController, CLLocationManagerDelegate { let locationManager = CLLocationManager() var currentLocation: CLLocation? override func viewDidLoad() { super.viewDidLoad() locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } // 获取到位置后的回调 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.last else { return } currentLocation = location // 拿到位置后可以停止更新,节省电量 locationManager.stopUpdatingLocation() } // 权限请求失败的回调 func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { if status != .authorizedWhenInUse && status != .authorizedAlways { // 提示用户开启权限 print("请开启位置权限以添加照片位置信息") } } }
2. 将CLLocation转换为EXIF兼容的GPS元数据字典
系统的CLLocation对象不能直接塞进照片元数据,需要转换成符合EXIF标准的GPS字典。写一个工具函数来处理:
func gpsMetadata(from location: CLLocation) -> [String: Any] { let coordinate = location.coordinate let latitudeRef = coordinate.latitude >= 0 ? "N" : "S" let longitudeRef = coordinate.longitude >= 0 ? "E" : "W" // 把经纬度转换成度分秒格式(EXIF要求的格式) func convertCoordinate(_ coordinate: CLLocationDegrees) -> [NSNumber] { let degrees = floor(abs(coordinate)) let minutes = floor((abs(coordinate) - degrees) * 60) let seconds = ((abs(coordinate) - degrees) * 60 - minutes) * 60 return [NSNumber(value: degrees), NSNumber(value: minutes), NSNumber(value: seconds)] } var gpsDict: [String: Any] = [ kCGImagePropertyGPSLatitude as String: convertCoordinate(coordinate.latitude), kCGImagePropertyGPSLatitudeRef as String: latitudeRef, kCGImagePropertyGPSLongitude as String: convertCoordinate(coordinate.longitude), kCGImagePropertyGPSLongitudeRef as String: longitudeRef, kCGImagePropertyGPSTimeStamp as String: location.timestamp, kCGImagePropertyGPSAltitude as String: NSNumber(value: location.altitude) ] // 如果有方向信息,也可以加上 if location.course >= 0 { gpsDict[kCGImagePropertyGPSImgDirection as String] = NSNumber(value: location.course) gpsDict[kCGImagePropertyGPSImgDirectionRef as String] = "T" // 真北方向 } return gpsDict }
3. 在捕获照片时嵌入GPS元数据
有两种方式可以把GPS元数据加到照片里,选一种你觉得方便的就行:
方式一:创建CaptureSettings时直接设置元数据
在准备拍摄照片的地方,创建AVCapturePhotoSettings时把GPS元数据加进去:
func takePhoto() { guard let currentLocation = currentLocation else { print("未获取到位置,无法添加GPS数据") return } let photoSettings = AVCapturePhotoSettings() // 把GPS元数据塞进settings的metadata里 let gpsDict = gpsMetadata(from: currentLocation) photoSettings.metadata[AVMetadataKeyGPSDictionary] = gpsDict // 开始捕获照片 if let photoOutput = captureSession.outputs.first as? AVCapturePhotoOutput { photoOutput.capturePhoto(with: photoSettings, delegate: self) } }
方式二:捕获完成后合并元数据再保存
如果在拍摄前还没拿到位置,或者想后期处理元数据,可以在捕获完成的回调里合并:
extension YourCameraViewController: AVCapturePhotoCaptureDelegate { func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { guard error == nil, let imageData = photo.fileDataRepresentation() else { print("照片捕获失败:\(error?.localizedDescription ?? "未知错误")") return } // 合并GPS元数据 var mergedMetadata = photo.metadata if let currentLocation = currentLocation { let gpsDict = gpsMetadata(from: currentLocation) mergedMetadata[AVMetadataKeyGPSDictionary] = gpsDict } // 保存照片到相册 PHPhotoLibrary.shared().performChanges({ let creationRequest = PHAssetCreationRequest.forAsset() creationRequest.addResource(with: .photo, data: imageData, options: nil) creationRequest.location = currentLocation // 这里也可以直接设置PHAsset的location属性,相册会自动识别 // 或者把合并后的元数据传进去 creationRequest.metadata = mergedMetadata }) { success, error in if success { print("照片保存成功,已包含GPS数据") } else { print("照片保存失败:\(error?.localizedDescription ?? "未知错误")") } } } }
小提示
- 测试的时候,可以拍完照片后在系统相册里查看照片的“信息”,里面会显示拍摄位置;或者用第三方工具(比如ExifViewer)查看EXIF信息,确认GPS数据是否存在。
- 如果用户拒绝了位置权限,记得处理这种情况,比如提示用户开启权限,或者跳过添加位置数据的逻辑。
内容的提问来源于stack exchange,提问作者DJFriar




