2017-06-13 59 views
4

在我的應用程序中,我想讓它成爲可能,即用戶爲他的PhotoLibrary中的任何圖像設置從0到5的StarRating。我的研究表明,有一對夫婦的方式來完成這件事:從現有phAsset修改元數據似乎不起作用

Save the exif metadata using the new PHPhotoLibrary

Swift: Custom camera save modified metadata with image

Writing a Photo with Metadata using Photokit

大多數的答案被創建一個新的照片。我的片段,現在看起來是這樣的:

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]的值是零。

我也嘗試了上面發佈的答案中的例子,但我總是得到相同的結果。

我希望有人知道,我可以做些什麼來改變元數據。

另外,如何使用MediaType .videoPHAsset更改元數據?我試圖通過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) 
    } 
}) 
+0

對於那些有興趣的人,我有一個開放的TSI票據,因此可能蘋果知道如何實現這一點。 –

+0

我爲[視頻元數據]打開了一個新問題(https://stackoverflow.com/questions/44824936/modifying-metadata-from-phasset-with-mediatype-video-fails) –

回答

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中。

+0

我得到***終止應用程序由於未捕獲的異常'NSInternalInconsistencyException',原因:' - [__ NSCFDictionary setObject:forKey:]:在行'imageProperties [kCGImagePropertyIPTCDictionary] = iptc' –

+0

發送到不可變對象的變異方法您是否正在檢查可以將imageProperties字典轉換爲一個NSMutableDictionary? – Spads

+0

是的,我得到一個NSMutableDictionary的ImageProperties –