将更新的图像保存到PhotoKit

时间:2016-12-15 16:39:40

标签: ios swift3 core-graphics photosframework

我在更新图片的元数据并将其保存回照片库时遇到问题。 的所有内容除了更改后的图像元数据缺少之前的条目,我没有在操作图像或执行照片库更改块时出现任何错误。此外,在它回写到图像之前的字典看起来像原始加上调试器中的字典。

我的问题是:

  1. 我做错了什么会导致重写现有的错误 物业回来与额外的数据,以消除那里有什么?
  2. 这样做有更好,更规范的方法吗?更新图像中的某些元数据似乎有很多机制。它似乎就像 别人在做什么。
  3. 修改

    在保存之前,所有Exif和Tiff值都存在。这是使用以下代码保存到照片后的整个元数据:

    ["PixelHeight": 2448, "PixelWidth": 3264, "{Exif}": {
    ColorSpace = 1;
    PixelXDimension = 3264;
    PixelYDimension = 2448;}, "Depth": 8, "ProfileName": sRGB IEC61966-2.1, "Orientation": 1, "{TIFF}": {
    Orientation = 1;}, "ColorModel": RGB, "{JFIF}": {
    DensityUnit = 0;
    JFIFVersion =     (
        1,
        0,
        1
    );
    XDensity = 72;
    YDensity = 72;}]
    

    代码,全部在Swift 3中,在iOS 10.1上进行测试

    基本工作流程是:

        // Get a mutable copy of the existing Exif meta
        let mutableMetaData = getMutableMetadataFrom(imageData: data)
    
        // Check to see if it has the {GPS} entry, if it does just exit.
        if let _ = mutableMetaData[kCGImagePropertyGPSDictionary as String] {
           callback(imageAsset, true, nil)
           return
        }
    
        // Add the {GPS} tag to the existing metadata
        let clLocation = media.location!.asCLLocation()
        mutableMetaData[kCGImagePropertyGPSDictionary as String] =
           clLocation.asGPSMetaData()
    
        // Attach the new metadata to the existing image
        guard let newImageData = attach(metadata: mutableMetaData, toImageData: data) else {
                callback(imageAsset, false, nil)
                return
        }
    
        let editingOptions = PHContentEditingInputRequestOptions()
        imageAsset.requestContentEditingInput(with: editingOptions) { editingInput, info in
            guard let editingInput = editingInput else { return }
            let library = PHPhotoLibrary.shared()
            let output = PHContentEditingOutput(contentEditingInput: editingInput)
            output.adjustmentData = PHAdjustmentData(formatIdentifier: "Project", formatVersion: "0.1",
                                                     data: "Location Adjustment".data(using: .utf8)!)
            do {
                try newImageData.write(to: output.renderedContentURL, options: [.atomic])
            } catch {
                callback(imageAsset, false, error)
                return
            }
    
            library.performChanges({
                let changeRequest = PHAssetChangeRequest(for: imageAsset)
                changeRequest.location = clLocation
                changeRequest.contentEditingOutput = output
    
            }, completionHandler: { success, error in ... ...
    

    工作流程的辅助方法是:

    func attach(metadata: NSDictionary, toImageData imageData:Data) -> Data? {
    
        guard
            let imageDataProvider = CGDataProvider(data: imageData as CFData),
            let cgImage = CGImage(jpegDataProviderSource: imageDataProvider, decode: nil,
                                  shouldInterpolate: true, intent: .defaultIntent),
            let newImageData = CFDataCreateMutable(nil, 0),
            let type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
                                                             "image/jpg" as CFString, kUTTypeImage),
            let destination = CGImageDestinationCreateWithData(newImageData,
                                                               (type.takeRetainedValue()), 1, nil) else {
    
                return nil
            }
    
        CGImageDestinationAddImage(destination, cgImage, metadata as CFDictionary)
        CGImageDestinationFinalize(destination)
    
        guard
            let newProvider = CGDataProvider(data: newImageData),
            let newCGImage = CGImage(jpegDataProviderSource: newProvider, decode: nil,
                                     shouldInterpolate: false, intent: .defaultIntent) else {
    
                return nil
        }
    
        return UIImageJPEGRepresentation(UIImage(cgImage: newCGImage), 1.0)
    }
    
    func getMutableMetadataFrom(imageData data : Data) -> NSMutableDictionary {
    
        let imageSourceRef = CGImageSourceCreateWithData(data as CFData, nil)
        let currentProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef!, 0, nil)
        let mutableDict = NSMutableDictionary(dictionary: currentProperties!)
    
        return mutableDict
    }
    

    另外,asGPSMetaDataCLLocation上的扩展名,而不是this Gist的Swift 3版本

1 个答案:

答案 0 :(得分:5)

事实证明,使用CoreGraphics来处理图像或元数据根本不是问题,这是我忽略的一些事情:

  1. 根据包含EXIF或GPS数据的数据构建UIImage 删除该数据...实际上,它删除了的元数据 除了一组核心的JFIF和大小数据(使用JPEG时)。回想起来它是有道理的,因为它们的内部表示只是原始数据。但是,我没有在文档中找到任何关于元数据的明确声明。
  2. 鉴于前一点,主要有两种方法将数据(带元数据的图像)对象放入 照片库要么将其写入临时文件 并阅读它 PHAssetChangeRequest::creationRequestForAssetFromImage(atFileURL:) 或使用PHAssetCreationRequest::forAsset创建创建请求 然后使用PHAssetCreationRequest::addResource(with:data:options:) 将数据添加为照片。我选择了后者,因为它移动较少 份。
  3. 所以我猜所有这些都取代了漂亮,简洁的ALAssetsLibrary::writeImage(toSavedPhotosAlbum:metadata:completionBlock:)

    照片库的最终更改块最终是:

        var assetID: String?
        PHPhotoLibrary.shared().performChanges({ 
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: imageWithMetaData as Data, options: nil)
            creationRequest.location = clLocation
            assetID = creationRequest.placeholderForCreatedAsset?.localIdentifier
        }) { success, error  in ...