【问题标题】:Modifing metadata from existing phAsset seems not working从现有 phAsset 修改元数据似乎不起作用
【发布时间】:2017-11-15 00:22:41
【问题描述】:

在我的应用程序中,我想让它成为可能,用户为他在他的 PhotoLibrary 中拥有的任何图像设置一个从 0 到 5 的 StarRating。我的研究表明,有几种方法可以做到这一点:

Save the exif metadata using the new PHPhotoLibrary

Swift: Custom camera save modified metadata with image

Writing a Photo with Metadata using Photokit

这些答案中的大多数都是在创建新照片。我的 sn-p 现在看起来像这样:

let options = PHContentEditingInputRequestOptions()
options.isNetworkAccessAllowed = true

self.requestContentEditingInput(with: options, completionHandler: {
            (contentEditingInput, _) -> Void in

    if contentEditingInput != nil {

        if let url = contentEditingInput!.fullSizeImageURL {
            if let nsurl = url as? NSURL {
                if let imageSource = CGImageSourceCreateWithURL(nsurl, nil) {
                    var imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary?
                    if imageProperties != nil {
                        imageProperties![kCGImagePropertyIPTCStarRating] = rating as AnyObject

                        let imageData = NSMutableData(contentsOf: url)
                        let image = UIImage(contentsOfFile: url.path)

                        let destination = CGImageDestinationCreateWithData(imageData!, CGImageSourceGetType(imageSource)!, 1, nil)

                        CGImageDestinationAddImage(destination!, image!.cgImage!, imageProperties! as CFDictionary)

                        var contentEditingOutput : PHContentEditingOutput? = nil

                        if CGImageDestinationFinalize(destination!) {
                            let archievedData = NSKeyedArchiver.archivedData(withRootObject: rating)
                            let identifier = "com.example.starrating"
                            let adjustmentData = PHAdjustmentData(formatIdentifier: identifier, formatVersion: "1.0", data: archievedData)

                            contentEditingOutput = PHContentEditingOutput(contentEditingInput: contentEditingInput!)
                            contentEditingOutput!.adjustmentData = adjustmentData
                            if imageData!.write(to: contentEditingOutput!.renderedContentURL, atomically: true) {
                                PHPhotoLibrary.shared().performChanges({
                                    let request = PHAssetChangeRequest(for: self)
                                    request.contentEditingOutput = contentEditingOutput
                                }, completionHandler: {
                                    success, error in
                                    if success && error == nil {
                                        completion(true)
                                    } else {
                                        completion(false)
                                    }
                                })
                            }
                        } else {
                            completion(false)
                        }

                    }
                }
            }
        }
    }
})

现在,当我想从 PHAsset 读取元数据时,我再次请求 ContentEditingInput 并执行以下操作:

if let url = contentEditingInput!.fullSizeImageURL {
    if let nsurl = url as? NSURL {
        if let imageSource = CGImageSourceCreateWithURL(nsurl, nil) {
            if let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary? {

                if let starRating = imageProperties[kCGImagePropertyIPTCStarRating] as? Int {
                    rating = starRating
                }
            }
        }
    }
}

但我从来没有得到我的评分,因为它说imageProperties[kCGImagePropertyIPTCStarRating] 的值为零。

我也尝试了上面发布的答案中的示例,但我总是得到相同的结果。

我希望任何人都知道,我可以做些什么来更改元数据。

另外,如何将元数据从 PHAsset 更改为 MediaType .video?我试图通过AVAssetWriterAVExportSession 对象来实现这一点,但在这两种情况下它都不起作用。这是我尝试的视频:

var exportSession = AVAssetExportSession(asset: asset!, presetName: AVAssetExportPresetPassthrough)
exportSession!.outputURL = outputURL
exportSession!.outputFileType = AVFileTypeQuickTimeMovie
exportSession!.timeRange = CMTimeRange(start: start, duration: duration)

var modifiedMetadata = asset!.metadata

let metadataItem = AVMutableMetadataItem()
metadataItem.keySpace = AVMetadataKeySpaceQuickTimeMetadata
metadataItem.key = AVMetadataQuickTimeMetadataKeyRatingUser as NSCopying & NSObjectProtocol
metadataItem.value = rating as NSCopying & NSObjectProtocol

modifiedMetadata.append(metadataItem)

exportSession!.metadata = modifiedMetadata


exportSession!.exportAsynchronously(completionHandler: {
    let status = exportSession?.status
    let success = status == AVAssetExportSessionStatus.completed
    if success {
        do {
            let sourceURL = urlAsset.url
            let manager = FileManager.default
            _ = try manager.removeItem(at: sourceURL)
            _ = try manager.moveItem(at: outputURL, to: sourceURL)
        } catch {
            LogError("\(error)")
            completion(false)
        }
    } else {
        LogError("\(exportSession!.error!)")
        completion(false)
    }
})

【问题讨论】:

  • 对于其他对此感兴趣的人,我有一个开放的 TSI 票,所以苹果可能知道如何实现这一目标。
  • 我为Video Metadata开了一个新问题

标签: ios swift phasset photokit


【解决方案1】:

抱歉,这不是一个完整的答案,但它涵盖了您问题的一部分。我注意到您将 StarRating 放在了错误的位置。您需要将它放在 IPTC 字典中。属性数据也存储为字符串。鉴于您拥有 imageProperties,您可以按如下方式添加星级并使用以下两个函数读取它

func setIPTCStarRating(imageProperties : NSMutableDictionary, rating : Int) {
    if let iptc = imageProperties[kCGImagePropertyIPTCDictionary] as? NSMutableDictionary {
        iptc[kCGImagePropertyIPTCStarRating] = String(rating)
    } else {
        let iptc = NSMutableDictionary()
        iptc[kCGImagePropertyIPTCStarRating] = String(rating)
        imageProperties[kCGImagePropertyIPTCDictionary] = iptc
    }
}


func getIPTCStarRating(imageProperties : NSMutableDictionary) -> Int? {
    if let iptc = imageProperties[kCGImagePropertyIPTCDictionary] as? NSDictionary {
        if let starRating = iptc[kCGImagePropertyIPTCStarRating] as? String {
            return Int(starRating)
        }
    }
    return nil
}

由于您从图像中获取的 imageProperties 不是可变的,因此您需要先创建这些属性的可变副本,然后才能调用上述函数。当您创建要保存的图像时,请在对 CGImageDestinationAddImage() 的调用中使用可变属性

if let mutableProperties = imageProperties.mutableCopy() as? NSMutableDictionary {
    setIPTCStarRating(imageProperties:mutableProperties, rating:rating)
}

另外一点,您正在创建一个不必要的 UIImage。如果您使用 CGImageDestinationAddImageFromSource() 而不是 CGImageDestinationAddImage() ,则可以使用之前创建的 imageSource 而不是将图像数据加载到 UIImage 中。

【讨论】:

  • 我在imageProperties[kCGImagePropertyIPTCDictionary] = iptc 线上收到*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
  • 您是否在检查是否可以将 imageProperties 字典转换为 NSMutableDictionary?
  • 是的,我得到了 ImageProperties 的 NSMutableDictionary
  • 我针对已经有 IPTC 字典的情况更改了 setIPTCStarRating()。你想现在试试吗?
  • 我不太确定,但似乎imageProperties[kCGImagePropertyIPTCDictionary] 是不可变的。因为我仍然得到错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-30
  • 1970-01-01
  • 2017-06-17
  • 2014-03-20
相关资源
最近更新 更多