Изменение метаданных из существующего phAsset не работает

В своем приложении я хочу сделать возможным, чтобы пользователь устанавливал звездный рейтинг от 0 до 5 для любого изображения, которое у него есть в его фотобиблиотеке. Мои исследования показывают, что есть несколько способов сделать это:

Сохраните метаданные exif, используя новую библиотеку PHPhotoLibrary

Swift: пользовательская камера сохраняет измененные метаданные с изображением

Запись фотографии с метаданными с помощью 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] равно нулю.

Я также пробовал примеры из ответов, которые я разместил выше, но всегда получаю тот же результат.

Надеюсь, кто-нибудь знает, что я могу сделать, чтобы изменить метаданные.

Кроме того, как я могу изменить метаданные из PHAsset с MediaType .video? Я пытался добиться этого через Объекты AVAssetWriter и AVExportSession, но в обоих случаях это не работает. Вот что я пробовал для видео:

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

person Marcel T    schedule 13.06.2017    source источник
comment
для всех, кто заинтересован в этом, у меня есть открытый билет TSI для этого, так что, вероятно, Apple знает, как этого добиться.   -  person Marcel T    schedule 27.06.2017
comment
Я открыл новый вопрос для метаданных видео.   -  person Marcel T    schedule 29.06.2017


Ответы (1)


Извините, это не полный ответ, но он охватывает одну часть вашего вопроса. Я заметил, что вы размещаете StarRating не в том месте. Вам нужно поместить его в словарь IPTC. Также данные свойств хранятся в виде строк. Учитывая, что у вас есть свойства изображения, вы можете добавить звездный рейтинг следующим образом и прочитать его, используя следующие две функции.

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
}

Поскольку свойства изображения, которые вы получаете из изображения, не изменяются, вам необходимо сначала создать изменяемую копию этих свойств, прежде чем вы сможете вызывать функции, описанные выше. Когда вы создаете изображение для сохранения, используйте изменяемые свойства в вызове CGImageDestinationAddImage().

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

Еще один момент, когда вы создаете ненужный UIImage. Если вы используете CGImageDestinationAddImageFromSource() вместо CGImageDestinationAddImage(), вы можете использовать ранее созданный imageSource вместо загрузки данных изображения в UIImage.

person adamfowlerphoto    schedule 29.06.2017
comment
Я получаю *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object' в строке imageProperties[kCGImagePropertyIPTCDictionary] = iptc - person Marcel T; 29.06.2017
comment
Вы проверяете, можете ли вы использовать словарь imageProperties как NSMutableDictionary? - person adamfowlerphoto; 29.06.2017
comment
да, я получаю NSMutableDictionary для ImageProperties - person Marcel T; 29.06.2017
comment
Я изменил setIPTCStarRating() для ситуации, когда уже есть словарь IPTC. Хотите попробовать сейчас? - person adamfowlerphoto; 29.06.2017
comment
я не совсем уверен, но кажется, что imageProperties[kCGImagePropertyIPTCDictionary] неизменяем. потому что я все еще получаю ошибку. - person Marcel T; 29.06.2017
comment
Отредактировано для включения изменяемой копии свойств перед настройкой данных IPTC. - person adamfowlerphoto; 29.06.2017
comment
Теперь это сработало! Я только что внес небольшие коррективы в ваш ответ, так как сейчас передаю NSNumber в словарь. Поэтому я могу преобразовать его непосредственно в Int, когда захочу получить его из изображения. Так что большое спасибо за это, вы не знаете, что мне нужно изменить, чтобы изменить метаданные видео? - person Marcel T; 29.06.2017
comment
Извините, ничего не знаю о метаданных видео. Я думаю, вам следует разбить это на другой вопрос. - person adamfowlerphoto; 29.06.2017