如何从iOS照片库中直接获取城市与州等地名信息?
iOS相册直接读取照片内置地名元数据方案
我在MacOS上可以通过查询Photos.sqlite数据库获取9万张照片的地名信息,但在iOS上目前只能拿到照片的经纬度坐标。我知道用reverseGeocodeLocation可以通过坐标转CLPlacemark,但这个接口有应用级速率限制。观察iPhone相册发现照片本身就带地名元数据,请问有没有对应的API能直接读取这些信息?
当前我的Swift代码:
private func fetchPhotoLocations() { let fetchOptions = PHFetchOptions() fetchOptions.includeHiddenAssets = false fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: fetchOptions) assets.enumerateObjects { (asset, index, stop) in if let location = asset.location { let resources = PHAssetResource.assetResources(for: asset) let filename = resources.first?.originalFilename ?? "Unknown" let options = PHAssetResourceRequestOptions() options.isNetworkAccessAllowed = false if let resource = resources.first { PHAssetResourceManager.default().requestData(for: resource, options: options) { (data) in } completionHandler: { (error) in if let error = error { print("Error loading resource: \(error)") } } DispatchQueue.main.async { self.photoLocations.append(PhotoLocation( id: asset.localIdentifier, coordinate: location.coordinate, creationDate: asset.creationDate ?? Date(), filename: filename )) } } } } }
直接读取内置地名的两种方法
Photos框架的PHAsset没有直接暴露地名属性,但照片的EXIF/GPS元数据里确实存储了已解析的地名信息,可通过以下方式提取:
1. 从PHAssetResource数据解析元数据
你当前的代码已经在请求资源数据,但没做解析。可以用CGImageSource从数据中提取GPS相关字段:
private func fetchPhotoLocations() { let fetchOptions = PHFetchOptions() fetchOptions.includeHiddenAssets = false fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: fetchOptions) assets.enumerateObjects { (asset, index, stop) in guard let location = asset.location else { return } let resources = PHAssetResource.assetResources(for: asset) let filename = resources.first?.originalFilename ?? "Unknown" let options = PHAssetResourceRequestOptions() options.isNetworkAccessAllowed = false guard let resource = resources.first else { return } PHAssetResourceManager.default().requestData(for: resource, options: options) { data in // 解析图片元数据 guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else { return } let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [String: Any] let gpsProperties = properties?[kCGImagePropertyGPSDictionary as String] as? [String: Any] // 提取地名相关字段 let locationName = gpsProperties?[kCGImagePropertyGPSLocationName as String] as? String let city = gpsProperties?[kCGImagePropertyGPSCity as String] as? String let country = gpsProperties?[kCGImagePropertyGPSCountry as String] as? String DispatchQueue.main.async { self.photoLocations.append(PhotoLocation( id: asset.localIdentifier, coordinate: location.coordinate, creationDate: asset.creationDate ?? Date(), filename: filename, locationName: locationName, city: city, country: country )) } } completionHandler: { error in if let error = error { print("Error loading resource: \(error)") } } } }
2. 通过PHContentEditingInput获取元数据(更高效)
这种方式不需要读取整个文件数据,直接通过资源URL解析,适合批量处理大量照片:
private func fetchPhotoLocations() { let fetchOptions = PHFetchOptions() fetchOptions.includeHiddenAssets = false fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: fetchOptions) assets.enumerateObjects { (asset, index, stop) in guard let location = asset.location else { return } let filename = asset.value(forKey: "filename") as? String ?? "Unknown" let editOptions = PHContentEditingInputRequestOptions() editOptions.isNetworkAccessAllowed = false asset.requestContentEditingInput(with: editOptions) { input, info in guard let input = input, let imageURL = input.fullSizeImageURL else { return } guard let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, nil) else { return } let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [String: Any] let gpsDict = properties?[kCGImagePropertyGPSDictionary as String] as? [String: Any] let locationName = gpsDict?[kCGImagePropertyGPSLocationName as String] as? String let city = gpsDict?[kCGImagePropertyGPSCity as String] as? String let country = gpsDict?[kCGImagePropertyGPSCountry as String] as? String DispatchQueue.main.async { self.photoLocations.append(PhotoLocation( id: asset.localIdentifier, coordinate: location.coordinate, creationDate: asset.creationDate ?? Date(), filename: filename, locationName: locationName, city: city, country: country )) } } } }
注意事项
- 部分照片可能未写入地名元数据,此时仍需降级使用
reverseGeocodeLocation,可通过批量缓存、分时段请求规避速率限制。 - 不同设备/拍摄软件的EXIF字段可能有差异,需多测试几种字段(比如部分照片会把地名存在自定义字段里)。
- 必须确保已获取
PHPhotoLibrary的访问权限,否则无法读取照片资源。
内容的提问来源于stack exchange,提问作者Ashbury
相关产品推荐
相关产品推荐

