Photo Editing ExtensionはiOS標準の「写真」アプリから呼び出せる、画像編集機能を提供するエクステンションです。
写真にVignette効果を適用するサンプルを作成しました。
https://github.com/imk2o/Try-AppExtensions
処理の流れ
- ビューコントローラがインスタンス化される
- 以前の補正データに対応しているか返答する
- PHContentEditingController#canHandleAdjustmentData(_:)
- 編集開始の準備をする
- PHContentEditingController#startContentEditingWithInput(_ contentEditingInput: PHContentEditingInput!, placeholderImage placeholderImage: UIImage!)
- 編集を完了する
- PHContentEditingController#finishContentEditingWithCompletionHandler(_:)
- PHContentEditingController#cancelContentEditing()
canHandleAdjustmentData(_:)
以前の補正データが読み込めるのであればtrueを返します。バージョンや補正データの内容から判断することになるはず。
func canHandleAdjustmentData(adjustmentData: PHAdjustmentData?) -> Bool {
guard let adjustmentData = adjustmentData else {
return false
}
// IDとバージョンが一致しているか?
return adjustmentData.formatIdentifier == self.formatIdentifier && adjustmentData.formatVersion == self.formatVersion
}
startContentEditingWithInput(_ contentEditingInput: PHContentEditingInput!, placeholderImage placeholderImage: UIImage!)
引数contentEditingInput
は完了時に必要なので握っておきます。
placeholderImage
は、プレビュー表示用のイメージが完成するまでの一時的な表示に利用できます。
(前回の編集が適用された画像のため、プレビュー用の入力画像には使用しないほうがいいです)
func startContentEditingWithInput(contentEditingInput: PHContentEditingInput?, placeholderImage: UIImage) {
self.input = contentEditingInput
// 前回編集時の設定したパラメータを取得し、スライダーに反映
let parameters = NSKeyedUnarchiver.unarchiveObjectWithData(self.input.adjustmentData.data) as? [String: AnyObject]
if let inputIntensity = parameters?["inputIntensity"] as? Float {
self.slider.value = inputIntensity
}
// プレビュー用の入力CI画像を生成しておく
if let CGImage = self.input.displaySizeImage?.CGImage {
self.ciInputImage = CIImage(CGImage: CGImage)
}
self.updatePreview()
}
finishContentEditingWithCompletionHandler(_:)
テンプレートどおり、バックグラウンドで最終画像の出力を行います。
input.fullSizeImageURL
からオリジナル画像を取得し、編集を適用した画像ファイルを
output.renderedContentURL
に出力します。
output.adjustmentData
に補正データを出力しておけば、ユーザが再度編集を行う際にパラメータを復元できます。nil
にはできませんが、空のNSData
を渡してもエクステンションは機能します。
func finishContentEditingWithCompletionHandler(completionHandler: ((PHContentEditingOutput!) -> Void)!) {
// Render and provide output on a background queue.
dispatch_async(dispatch_get_global_queue(CLong(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0)) {
let output = PHContentEditingOutput(contentEditingInput: self.input)
let inputIntensity = self.currentValue
// 編集パラメータを構築
output.adjustmentData = PHAdjustmentData(
formatIdentifier: self.formatIdentifier,
formatVersion: self.formatVersion,
data: NSKeyedArchiver.archivedDataWithRootObject(["inputIntensity": inputIntensity])
)
// オリジナル画像を取得し、フィルタを適用
guard
let inputImageURL = self.input.fullSizeImageURL,
let ciInputImage = CIImage(contentsOfURL: inputImageURL)
else {
return // FIXME
}
let filteredImage = ciInputImage.createVignetteFilteredImage(inputIntensity, context: self.ciContext)
// JPEGファイルを出力 (TODO: CIImageから直接JPEGファイルを出力する方法はあるのか?)
guard let JPEGData = UIImageJPEGRepresentation(filteredImage, 0.8) else {
return // FIXME
}
JPEGData.writeToURL(output.renderedContentURL, atomically: true)
// Call completion handler to commit edit to Photos.
completionHandler?(output)
}
}
メモ
写真に編集を適用すると、オリジナル画像はオモテに表示されなくなります。
データ自体は残っており、ユーザが「元に戻す」を行うことでオリジナル画像が復元されます。
参考