如何使用UIImagePickerController为拍摄的照片设置位置信息
解决方案
一、保存相册图片时保留GPS元数据
直接保存UIImage对象会丢失EXIF元数据(包括GPS信息),因为UIImage仅存储像素数据,不包含图片的原始元数据。必须获取原始图片的完整数据(含元数据)再写入文件:
实现步骤(Swift)
- 通过
info[UIImagePickerController.InfoKey.referenceURL]获取相册图片的Asset URL - 借助
PHAsset读取图片的原始数据与元数据 - 将带元数据的图片数据写入Documents文件夹
import Photos func saveAlbumImageWithMetadata(info: [UIImagePickerController.InfoKey: Any]) { guard let assetURL = info[.referenceURL] as? URL else { return } let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [assetURL], options: nil) guard let asset = fetchResult.firstObject else { return } let options = PHImageRequestOptions() options.isSynchronous = true options.deliveryMode = .highQualityFormat options.version = .original PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { imageData, _, _, info in guard let data = imageData else { return } // 写入Documents目录 let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let saveURL = docsDir.appendingPathComponent("saved_album_image.jpg") do { try data.write(to: saveURL) } catch { print("保存失败:\(error)") } } }
二、为应用内拍摄的照片添加GPS位置信息
拍照后info[.referenceURL]为nil是因为照片尚未存入系统相册,需主动获取当前位置,将GPS信息写入图片的EXIF元数据后再保存:
实现步骤(Swift)
- 在
Info.plist中添加位置权限描述(NSLocationWhenInUseUsageDescription) - 获取当前经纬度坐标
- 将GPS信息转换为EXIF格式的元数据
- 合并图片数据与元数据后保存
import CoreLocation import ImageIO // 获取当前位置 func getCurrentLocation(completion: @escaping (CLLocationCoordinate2D?) -> Void) { let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() guard CLLocationManager.locationServicesEnabled() else { completion(nil) return } locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.delegate = self locationManager.requestLocation() } // 为图片添加GPS元数据 func addGPSMetadata(to imageData: Data, coordinate: CLLocationCoordinate2D) -> Data? { guard let source = CGImageSourceCreateWithData(imageData as CFData, nil), let uti = CGImageSourceGetType(source) else { return nil } let mutableData = NSMutableData(data: imageData) guard let destination = CGImageDestinationCreateWithData(mutableData, uti, 1, nil) else { return nil } // 获取原始元数据并注入GPS信息 var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any] ?? [:] let gpsMetadata: [CFString: Any] = [ kCGImagePropertyGPSLatitude: abs(coordinate.latitude), kCGImagePropertyGPSLatitudeRef: coordinate.latitude >= 0 ? "N" : "S", kCGImagePropertyGPSLongitude: abs(coordinate.longitude), kCGImagePropertyGPSLongitudeRef: coordinate.longitude >= 0 ? "E" : "W", kCGImagePropertyGPSTimeStamp: Date().iso8601String ] metadata[kCGImagePropertyGPSDictionary] = gpsMetadata // 写入带GPS元数据的图片 CGImageDestinationAddImageFromSource(destination, source, 0, metadata as CFDictionary) return CGImageDestinationFinalize(destination) ? mutableData as Data : nil } // Date扩展:生成ISO8601格式时间字符串 extension Date { var iso8601String: String { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter.string(from: self) } } // 保存拍摄的图片(在UIImagePickerControllerDelegate中调用) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) guard let image = info[.originalImage] as? UIImage, let imageData = image.jpegData(compressionQuality: 0.8) else { return } getCurrentLocation { coordinate in if let coord = coordinate, let dataWithGPS = self.addGPSMetadata(to: imageData, coordinate: coord) { self.saveImageToDocuments(data: dataWithGPS) } else { // 无位置权限时保存无GPS的图片 self.saveImageToDocuments(data: imageData) } } } // 写入Documents目录 func saveImageToDocuments(data: Data) { let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let saveURL = docsDir.appendingPathComponent("captured_image.jpg") do { try data.write(to: saveURL) } catch { print("保存失败:\(error)") } } // 实现CLLocationManagerDelegate协议 extension YourViewController: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.first else { return } manager.stopUpdatingLocation() // 此处可根据业务逻辑处理位置回调 } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("获取位置失败:\(error)") manager.stopUpdatingLocation() } }
注意事项
- 位置权限需提前申请,
Info.plist中必须添加对应权限描述,否则应用会崩溃 - 若需将带GPS的拍摄图片存入系统相册,可改用
PHPhotoLibrary写入带元数据的图片数据
内容的提问来源于stack exchange,提问作者Akhileshwar Babu
相关产品推荐
相关产品推荐

