You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动