La modification des métadonnées de phAsset existant ne semble pas fonctionner

Dans mon application, je veux rendre possible, que l'user définit un StarRating de 0 à 5 pour toute image qu'il a dans sa photothèque. Ma search montre qu'il y a plusieurs façons de faire cela:

Sauvegardez les métadonnées exif en utilisant la nouvelle bibliothèque PHPhotoLibrary

Swift: camera personnalisée save les métadonnées modifiées avec l'image

Ecrire une photo avec des métadonnées en utilisant Photokit

La plupart de ces réponses étaient en train de créer une nouvelle photo. Mon extrait ressemble maintenant à ceci:

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) } } } } } } }) 

Maintenant, lorsque je souhaite lire les métadonnées de PHAsset, je request à nouveau ContentEditingInput et procédez comme suit:

 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 } } } } } 

Mais je n'obtiens jamais ma note car elle indique que la valeur de imageProperties[kCGImagePropertyIPTCStarRating] est nulle.

J'ai aussi essayé les exemples des Réponses que j'ai postées ci-dessus, mais j'ai toujours le même résultat.

J'espère que tout le monde le sait, ce que je peux faire pour changer les métadonnées.

Aussi, comment puis-je changer les métadonnées d'un PHAsset avec le MediaType .video ? J'ai essayé d'atteindre cela à travers les objects AVAssetWriter et AVExportSession , mais dans les deux cas cela ne fonctionne pas. Voici ce que j'ai essayé pour les videos:

 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) } }) 

Désolé, ce n'est pas une réponse complète, mais cela couvre une partie de votre question. J'ai remarqué que vous placez le StarRating au mauvais endroit. Vous devez le placer dans un dictionary IPTC. Les données de propriétés sont également stockées en tant que strings. Étant donné que vous avez l'imageProperties, vous pouvez append la note d'écanvas comme suit et la lire en utilisant les deux fonctions suivantes

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

Comme les propriétés image que vous obtenez de l'image ne sont pas modifiables, vous devez d'abord créer une copy mutable de ces propriétés avant de pouvoir appeler les fonctions ci-dessus. Lorsque vous créez votre image pour save utilisez les propriétés mutables dans votre appel à CGImageDestinationAddImage ()

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

Un autre point que vous créez un UIImage inutile. Si vous utilisez CGImageDestinationAddImageFromSource () au lieu de CGImageDestinationAddImage (), vous pouvez utiliser l'imageSource que vous avez créé précédemment au lieu de charger datatables d'image dans un UIImage.