(原文:Understanding Photo Editing Extensions in iOS 8 作者:Joyce Echessa 譯者:X140Yu)
Photo Editing Extension 允許用戶在 Photos
應用裡使用第三方應用編輯照片或視頻。在此之前,用戶不得不先在相機應用裡拍攝照片,然後切換到照片編輯應用裡編輯,或者必須從相冊裡導入照片。現在,這步應用間的切換可以被省略了,用戶能不從照片應用切換出去就能編輯照片了。在 Photo Editing Extension
裡編輯完成並確認修改後,Photos 應用裡的圖片也會獲得同樣的調整。照片的初始版本也保存下來,這樣用戶可以隨時恢復在擴展應用裡做出的修改。
我們一起來看看怎樣制作一個 Photo Editing Extension。由於我們的關注點在創建應用程序擴展而不是寫一個完整的應用,我已經創建了一個你們可以下載並且一路跟著做的上手項目。
這是一個識別圖片上的臉然後自動給臉部打上馬賽克的簡單應用。在沒有識別出臉的情況下,圖片是不會被更改的。這將會成為我們擴展應用的母應用。擴展應用必須是母應用的一部分,不可能只有一個單單的擴展應用。如果想更多地了解一下擴展應用是什麼原理,你可以浏覽我們之前寫的關於Today extension的一篇文章。
我把圖片處理的代碼放在了一個框架裡。這樣我就可以在擴展應用和母應用裡使用同一份代碼。當你創建了一個應用擴展,它必須是它自己的 target,所以如果你想在主程序和擴展程序裡重用這些代碼,你必須把公共的代碼嵌入進框架中去。在下載的程序中,我已經創建了框架。如果你想了解如何創建,閱讀一下上一段提到的文章。
我們將創建一個對於其它應用也可以使用這種功能的擴展應用。
一開始,在項目導航中選擇 FaceBlur project,然後選擇 Editor > Add Target。選擇 iOS > Application Extension > Photo Editing Extension 然後點擊 Next。
把 Product Name 設置成 FaceBlur Filter,語言選擇 Swift,其它保持不變。
當詢問你時候是否啟用新解決方案的時候,選擇 Activate。
在項目導航器裡有一個標有 FaceBlur Filter 的新組。它包含三個文件 - PhotoEditingViewController.swift, MainInterface.storyboard 和 Info.plist。
在 MainInterface.storyboard 中,視圖構建器是我們在之前 iOS 版本中使用過的,它沒有自適應布局(adaptive layout)。View controller 是 iPhone 大小,並且在視圖構建器的底部沒有 Size Class 的控制區域。但是我們想創建一個可以適應各種不同尺寸屏幕的應用擴展程序。
在 Document Outline 中選擇 Photo Editing View Controller。在 File Inspector 中,把 Use Size Classes 打勾,並在彈出的窗口中選擇確認。
在 storyboard 中刪掉這個 View Controller,從 Object Library 裡拽一個新的 View Controller。正如你所見,它將是一個接近正方形的形狀而不是一個特定型號設備的形狀。
在 Identity inspector,把這個 View Controller 的類設置成 PhotoEditingViewController,並且在 Attributes Inspector 裡把 Is Initial View Controller 打勾。
在 Document Outline 裡,選中 View ,在 Attributes Inspector 裡把它的顏色設置成 Dark Gray Color。
向 View 的底部拽兩個按鈕。各放在左右。把它們的顏色設置成白色,並把左右按鈕的標題分別設置成 Cancel 和 Add Filter。現在你的 View 應該跟下圖的 View 差不多。
選中 Cancel 按鈕,向它的左側和底部添加約束。
選中 Add Filter 按鈕,向它的右側和底部添加約束。
向主 View 中拽一個 Image View。調整它的大小,來讓它的頂部,左部和右部邊緣能夠緊貼 View
的邊緣。拖住它的底部一直向下拉,直到在靠近按鈕的時候出現了藍色的導航線再停下。保持 Image View 為選中狀態,選擇 Pin 菜單,把
Constrain to margins 取消。把頂部,左側和右側的約束設置成0,對於底部的約束,單擊底部約束下拉菜單,選擇 Bottom
Layout Guide。添加這四個約束。
按住 Control 鍵,同時從 Image View 到 PhotoEditingViewController 類拖動鼠標,為 Image View 添加一個 ouelet(使用 Assistant editor,把左右兩個文件分別調整為 storyboard 和 類文件)。把 outlet 命名為 imageView。為底部的兩個按鈕分別添加 outlet,命名為 cancelButton 和 addFilterButton。
按住 Control 鍵,同時從 Cancel 按鈕到 PhotoEditingViewController 類拖動鼠標,把 Connection 改為 Action。把 Type 設置成 UIButton,命名它為 cancel。對於 Add Filter 也一樣,把這個 Action 命名為 addFilter。你現在類應該擁有如下的 outlets 和 actions。
@IBOutlet weak var imageView: UIImageView! @IBOutlet weak var cancelButton: UIButton! @IBOutlet weak var addFilterButton: UIButton! @IBAction func cancel(sender: UIButton) { } @IBAction func addFilter(sender: UIButton) { }
PhotoEditingViewController 類遵從了 PHContentEditingController 協議,在這個類裡會有幾個協議方法的實現。
startContentEditingWithInput(contentEditingInput:, placeholderImage:) - 這個方法在 View Controller 出現之前並且 asset 可以被編輯時被調用。它被傳入兩個對象 - 一個 PHContentEditingInput 實例(要被編輯的 asset)和一個 UIImage 實例(可能是原本的圖像或者根據下面提到的 canHandleAdjustmentData(adjustmentData:) 方法的返回值得到的被編輯過的圖像)。
finishContentEditingWithCompletionHandler(completionHandler:) - 這個方法在圖像被編輯以後被調用。一個存儲編輯和描述編輯調整的數據的 PHContentEditingOutput 實例會被創建。這個實例會被傳入 completionHandler。
cancelContentEditing – 這個方法在用戶取消編輯時被調用。你應當在這裡清理不需要的資源。
canHandleAdjustmentData(adjustmentData:) – 如果你的擴展應用能對之前的編輯生效,那這個方法返回 true。
我們將處理以上這幾個方法。
先導入 ImageProcessingKit 框架。在文件添加以下代碼。
import ImageProcessingKit
在類中添加以下兩個變量。
var processedImage: UIImage? let processor = ImageProcessor()
按照以下形式修改 addFilter()。
@IBAction func addFilter(sender: UIButton) { if let input = input { processedImage = processor.processImage(input.displaySizeImage) imageView.image = processedImage } }
這裡把顯示的照片改成了編輯後的照片而並沒有把改變應用於圖片。如果一個擴展應用真的提供了許多種濾鏡,那麼這裡可以讓用戶在選擇確定之前,來嘗試多種不同的濾鏡。
按照下面的形式修改 cancel(),當用戶點擊 Cancel 按鈕時,把 Image View 設置成原來的圖片。
@IBAction func cancel(sender: UIButton) { if let input = input { imageView.image = input.displaySizeImage processedImage = nil } }
按照以下形式修改 startContentEditingWithInput()。
func startContentEditingWithInput(contentEditingInput: PHContentEditingInput?, placeholderImage: UIImage) { input = contentEditingInput if let input = contentEditingInput { imageView.image = input.displaySizeImage } }
當編輯模式將要啟動時,這個方法能夠獲取輸入的圖片並把它顯示在 Image View 裡。
按照以下形式修改 finishContentEditingWithCompletionHandler() 。
func finishContentEditingWithCompletionHandler(completionHandler: ((PHContentEditingOutput!) -> Void)!) { if input == nil { self.cancelContentEditing() return } dispatch_async(dispatch_get_global_queue(CLong(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0)) { let contentEditingOutput = PHContentEditingOutput(contentEditingInput: self.input) let archiveData = NSKeyedArchiver.archivedDataWithRootObject("Face Blur") let identifier = "com.appcoda.FaceBlur.FaceBlur-filter" let adjustmentData = PHAdjustmentData(formatIdentifier: identifier, formatVersion: "1.0", data: archiveData) contentEditingOutput.adjustmentData = adjustmentData if let path = self.input!.fullSizeImageURL.path { var image = UIImage(contentsOfFile: path)! image = self.processor.processImage(image) let jpegData = UIImageJPEGRepresentation(image, 1.0) var error: NSError? let saveSucceeded = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: .DataWritingAtomic, error: &error) if saveSucceeded { completionHandler(contentEditingOutput) } else { println("Save error") completionHandler(nil) } } else { println("Load error") completionHandler(nil) } } }
首先,我們檢查一下是否有圖片被選中,如果沒有,什麼都不做,直接 return。如果有,我們啟動一個異步 block 來做修改的工作。
接著,通過傳進來的對象創建了一個 PHContentEditingOutput 實例。我們又創建了一個 PHAdjustmentData 實例。隨著修改的進行,調整的數據也會被一路保存下來。PHAdjustmentData 對象提供了一個你可以用來重構編輯的“菜譜”,如果你的擴展應用提供了多種不同的濾鏡,這個對象能把應用濾鏡的順序給保存下來,所以它們可以隨時恢復到之 前某一個已編輯圖片的狀態。這個數據需要一個獨一無二的標識和一個版本號碼。對於標識,我們把域名標識倒過來來表示,為了保證它的獨一無二性。版本號碼可 以幫助你擴展應用後面的版本不把你先前版本不同的給歸檔。
然後,我們把需要修改的整張圖片從磁盤中載入並應用濾鏡。然後,把這個數據轉換成 JPEG 格式,重新寫入磁盤。如果成功了,completion handler 會被調用,並且被傳入修改後的對象。
為了運行此擴展,先要檢察解決方案是不是被設置成 FaceBlur Filter。運行工程,會彈出一個窗口讓你選擇通過哪個應用來運行。我們選擇 Photos。
Photos 應用會啟動。選擇一張圖片並進入編輯模式。在底部的右下角,會有一個內含三個點的圓形圖標。
單擊它來激活第三方編輯圖片的應用程序擴展。
選擇 FaceBlur 然後點擊 Add Filter。如果檢測出了臉,這張圖片會被處理並且被編輯。你可以選擇取消修改或者選擇確認來保存這些修改。
在選擇確定以後,會返回 Photos 應用,這裡將顯示編輯過後的圖像。
你也可以在照片的編輯模式下按 Revert 來取消之前用濾鏡做的修改。
我們已經了解了如何創建一個簡單的照片編輯應用擴展了。你也可以把它變得更復雜:創建一個可以處理先前編輯的應用擴展。我希望這篇博文能在你處理你自己的應用擴展時,給你一個良好的參照。如果你想得到關於應用擴展的更多信息,一定要看看 App Extension Programming Guide。
為了讓你有個參考,你可以下載整個 Xcode 項目。
注意:當然運行這個應用擴展時,你可能會注意到一些錯誤還有 CoreMedia 的許可信息。這些在 Xcode 6 的 GM 版本發布後開始出現,所以在以後的版本可能不會出現,或者在 iOS 8 將來的版本會被修復。但是現在來看,除了這些問題,整個應用擴展運行良好。