在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 照片擴展
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應用數(shù)據(jù)腳本化
一個完整的 Core Data 應用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡應用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應式 Android 應用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應式視圖
Square Register 中的擴張
DTrace
基礎集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設計的藝術(shù)
導航應用
線程安全類的設計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

照片擴展

在 iOS 8 發(fā)布時,蘋果把六種全新擴展功能介紹給全世界,它們史無前例的提供了訪問操作系統(tǒng)的可行性?,F(xiàn)在,開發(fā)者可以利用照片擴展來為系統(tǒng)相機應用增加功能。

用戶使用照片編輯擴展的流程并不簡單。從選擇編輯的照片開始,需要點擊三次才能啟動,其中一步驟是非常小一個按鈕:

http://wiki.jikexueyuan.com/project/objc/images/21-13.png" alt="" />

然而,這類擴展給開發(fā)者提供了為用戶創(chuàng)造無縫體驗,創(chuàng)建一致的方法來管理照片的絕佳的機會。

本文在了解更詳細的編輯工作流程之前,將先簡單討論如何創(chuàng)建擴展以擴展的生命周期,我們會通過常見的相關問題和場景來創(chuàng)建照片編輯擴展從而得出結(jié)論。

本文的示例項目 Filtster 演示了如何創(chuàng)建自己的圖片編輯擴展。它詮釋使用了數(shù)個 Core Image 濾鏡完成簡單的圖像過濾效果。完整 Filtster 項目代碼 可以在 GitHub 上找到。

創(chuàng)建擴展

所有擴展必須包含在一個功能齊全的 iOS 應用程序之內(nèi),照片編輯擴展也不例外。這可能意味著你必須做很多令人吃驚的自定義 Core Image 濾鏡的才能讓它到達用戶手中。蘋果如何嚴格審查還有待觀察,因為蘋果商店內(nèi)的大多數(shù)有圖片編輯功能的應用都是在 iOS 8 引入之前就已經(jīng)存在了的。

為了創(chuàng)建新的圖片編輯擴展,需要為已有的 iOS 項目添加新的 target,擴展 target 模板如下:

http://wiki.jikexueyuan.com/project/objc/images/21-14.png" alt="" />

模板由三部分組成:

  1. Storyboard 圖片編輯擴展除了系統(tǒng)在頂部提供的 toolbar 包含取消以及完成按鈕之外,界面幾乎完全可以自定義。

http://wiki.jikexueyuan.com/project/objc/images/21-15.png" alt="" />

雖然 storyboard 默認不包含 size classes,系統(tǒng)將允許你選擇并激活該功能,雖然沒有明顯的原因來阻止你使用手動布局,但蘋果還是強烈建議使用 Auto Layout 來創(chuàng)建照片編輯擴展。如果你忽略蘋果的建議你將不得不面對很多潛在風險。
  1. Info.plist 設定擴展的類型和可被接受媒體的類型,以及擴展的通用配置。NSExtension 鍵值是一個字典,它包含擴展所需要的配置:

http://wiki.jikexueyuan.com/project/objc/images/21-16.png" alt="" />

`NSExtensionPointIdentifier` 實體告訴系統(tǒng)這是一個使用 `com.apple.photo-editing` 作為值的照片編輯擴展。唯一特殊的 key 是 `PHSupportedMediaTypes`,它指明可以被操作的媒體類型。在默認情況下,這是一個包含 `Image` 實體的數(shù)組,當然你也可添加 `Video` 選項。
  1. View Controller 遵守 PHContentEditingController 協(xié)議,其中包含了圖片編輯擴展需要的生命周期方法。更多詳情見本文下個部分。

值得注意的是不要忘記提供菜單內(nèi)的擴展的圖標:

http://wiki.jikexueyuan.com/project/objc/images/21-17.png" alt="" />

圖標通過宿主 app 的資源目錄內(nèi)的 App Icon 提供。這里文檔有些讓人迷惑,它暗示你必須在擴展本身里來提供圖標。然而,盡管我們可以提供一個這樣的圖標,但擴展將不會使用選擇它。這一點有些爭議,因為蘋果指定與擴展相關的圖標必須與容器應用程序的相同。

擴展的生命周期

照片編輯擴展建立于 Photos 框架之上的,這意味著編輯不是破壞性的。當一個照片資源被編輯的時候,原始文件始終沒有被修改,編輯的結(jié)果將作為副本被保存下來。另外,語義細節(jié)包含了如何重新編輯并保存調(diào)整后的數(shù)據(jù)。這個數(shù)據(jù)的意思是編輯可以基于原始文件重新來過。當你實現(xiàn)圖片編輯擴展的時候,你只負責構(gòu)建你自己的數(shù)據(jù)對象。

PHAdjustmentData 類含有編輯所需參數(shù),以及兩個格式化的屬性 (formatIdentifierformatVersion) 用來確定當前編輯擴展針對于之前的編輯過的照片的兼容性。它們兩個都是字符串類型,另外 formatIdentifier 規(guī)定為反向域名解析格式。這兩個屬性讓你靈活的創(chuàng)建一套圖像編輯的應用程序以及擴展,每一種都可以用另一種表示。另外 data 屬性是 NSData 類型??梢员挥脕戆茨愕男枰鎯U展操作的細節(jié),以便讓你的擴展能繼續(xù)編輯。

開始編輯

當用戶使用你的擴展來編輯照片的時候,系統(tǒng)會實例化你的視圖控制器并且初始化照片編輯的生命周期。如果照片之前曾經(jīng)被編輯,它首先會調(diào)用 canHandleAdjustmentData(_:) 方法,同時為你提供一個 PHAdjustmentData 對象。因此,你的擴展是否可以處理之前編輯過的數(shù)據(jù)就很重要,這將決定框架發(fā)送的下一個生命周期的方法是什么。

一旦系統(tǒng)決定提供原始圖片還是之前就被渲染編輯過的圖片,接下來將會調(diào)用 startContentEditingWithInput(_:, placeholderImage:)。輸入是一個類型為 PHContentEditingInput 的對象,其中包含了地理位置,創(chuàng)建時間以及媒體類型等來自于原始資源的元數(shù)據(jù),以及你需要編輯的資源細節(jié)。除了原始尺寸的輸入圖片的路徑以外,輸入對象還包含一個 displaySizedImage 表示相同的圖片數(shù)據(jù),但是根據(jù)屏幕尺寸進行了適當縮放。這意味著交互編輯可以在較低分辨率下進行,以此來確保擴展可以保持迅速響應操作并節(jié)省能量。

下面是實現(xiàn)方法

func startContentEditingWithInput(contentEditingInput: PHContentEditingInput?,
                                  placeholderImage: UIImage) {
  input = contentEditingInput
  filter.inputImage = CIImage(image: input?.displaySizeImage)
  if let adjustmentData = contentEditingInput?.adjustmentData {
    filter.importFilterParameters(adjustmentData.data)
  }

  vignetteIntensitySlider.value = Float(filter.vignetteIntensity)
  ...
}

上面的實現(xiàn)中存儲了 contentEditingInput,因為要完成編輯并從調(diào)整后的數(shù)據(jù)導入濾鏡參數(shù)的時候我們會需要它。

如果你的 canHandleAdjustmentData(_:) 返回 true,startContentEditingWithInput(_:, placeholderImage:) 將會提供原始圖片,然后你的擴展需要根據(jù)調(diào)整后的數(shù)據(jù)來重新創(chuàng)建編輯過的圖片。如果這是一個耗時操作,那么 placeholderImage 將提供一個上次編輯渲染后的臨時圖片來讓你暫時使用。

在這個階段,用戶將通過擴展界面的交互來控制編輯的進程。因為擴展包含一個視圖控制器,你可以使用任何 UIKit 來實現(xiàn)它。示例項目使用了 Core Image 的濾鏡鏈來完成編輯,所以界面使用了一個自定義的 GLKView 子類來減少 CPU 的負載。

取消編輯

在完成編輯時,用戶可以選擇照片界面提供的取消或者完成按鈕。如果想讓用戶確定是否取消尚未保存的編輯內(nèi)容,shouldShowCancelConfirmation 屬性需要重寫并返回 true

http://wiki.jikexueyuan.com/project/objc/images/21-18.png" alt="" />

如果需要取消操作,cancelContentEditing 方法將被調(diào)用來允許你清空所有臨時數(shù)據(jù)。

提交修改

一旦用戶決定保存編輯操作,并且點擊了完成按鈕,finishContentEditingWithCompletionHandler(_:) 將會被調(diào)用。在這個時候,原始尺寸圖像需要用與當前顯示的圖片相同設置來編輯,并保存調(diào)整后的數(shù)據(jù)。

在這時,你可以通過在編輯過程開始時提供的 PHContentEditingInput 對象內(nèi)的 fullSizeImageURL 來獲取原始尺寸的圖片。

要完成編輯,我們需要調(diào)用提供的回調(diào)函數(shù),并提供一個從輸入創(chuàng)建的 PHContentEditingOutput 對象。這個輸出對象還包含了一個 renderedContentURL 屬性,用來指定你應該把輸出的 JPEG 數(shù)據(jù)存放在哪里:

func finishContentEditingWithCompletionHandler(completionHandler: ((PHContentEditingOutput!) -> Void)!) {
  // 在后臺隊列渲染并提供輸出。
  dispatch_async(dispatch_get_global_queue(CLong(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0)) {

    // 從編輯輸入創(chuàng)建編輯輸出。

    let output = PHContentEditingOutput(contentEditingInput: self.input)

    // 提供調(diào)整后的數(shù)據(jù)并且渲染輸出到指定位置。   

    let adjustmentData = PHAdjustmentData(formatIdentifier: self.filter.filterIdentifier,
      formatVersion: self.filter.filterVersion, data: self.filter.encodeFilterParameters())
    output.adjustmentData = adjustmentData

    // 寫入 JPEG 圖片

    let fullSizeImage = CIImage(contentsOfURL: self.input?.fullSizeImageURL)
    UIGraphicsBeginImageContext(fullSizeImage.extent().size);
    self.filter.inputImage = fullSizeImage
    UIImage(CIImage: self.filter.outputImage)?.drawInRect(fullSizeImage.extent())

    let outputImage = UIGraphicsGetImageFromCurrentImageContext()
    let jpegData = UIImageJPEGRepresentation(outputImage, 1.0)
    UIGraphicsEndImageContext()

    jpegData.writeToURL(output.renderedContentURL, atomically: true)

    // 調(diào)用完成回調(diào)提交編輯后的圖片。

    completionHandler?(output)
  }
}

一旦對 completionHandler 返回,你就可以清空臨時數(shù)據(jù),并且修改后的文件已經(jīng)準備好從擴展返回。

常見問題

與創(chuàng)建圖片編輯擴展相關的內(nèi)容其中一些可能有些復雜,本節(jié)內(nèi)容將介紹最重要的幾個。

調(diào)整數(shù)據(jù) (Adjustment Data)

PHAdjustmentData 是一個只包含三個屬性的簡單類,但是想要用好的話,依然需要遵循一些規(guī)則。蘋果建議使用反向域名解析格式來指定 formatIdentifier,但是 formatVersiondata 如何使用將由你自己決定。

重要的是要確保你不同版本圖片編輯擴展的兼容性,所以我們需要類似語義化版本這樣能提供靈活的管理產(chǎn)品的生命周期的方式。你可以以自己的方式進行解析,也可以依賴于像 SemverKit 之類的第三方框架提供的功能。

最后對于調(diào)整數(shù)據(jù)要說的是 data 本身,它是一個 NSData 數(shù)據(jù)對象。蘋果提供的唯一建議是它應該用來存放重建編輯時所需要的的設定,而不是編輯本身,這是因為 PHAdjustmentData 對象的尺寸是受 Photo 框架限制的。

對于不是很復雜的擴展 (比如 Filtster),這個數(shù)據(jù)可以是簡單地對一個字典歸檔,代碼如下:

public func encodeFilterParameters() -> NSData {
  var dataDict = [String : AnyObject]()
  dataDict["vignetteIntensity"] = vignetteIntensity
  ...
  return NSKeyedArchiver.archivedDataWithRootObject(dataDict)
}

接著提供解析方式:

public func importFilterParameters(data: NSData?) {
  if let data = data {
    if let dataDict = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [String : AnyObject] {
      vignetteIntensity = dataDict["vignetteIntensity"] as? Double ?? vignetteIntensity
      ...
    }
  }
}

這里,這兩個方法存在于共享的 FiltsterFilter 類中,同時這個類也負責確定調(diào)整數(shù)據(jù)的兼容性:

public func supportsFilterIdentifier(identifier: String, version: String) -> Bool 
  return identifier == filterIdentifier && version == filterVersion
}

如果你有更復雜的需求,你可以創(chuàng)建一個自定義的設置類,讓它支持 NSCoding 協(xié)議并用類似的方式進行歸檔。

用戶需要可以將互不兼容的照片編輯串聯(lián)起來 -- 如果當前擴展無法理解調(diào)整數(shù)據(jù)的話,一張預先渲染好的圖像將被作為輸入。比如你可以使用系統(tǒng)的裁剪工具先對圖片進行裁剪,然后再在你的自定義照片編輯擴展中使用。在你存儲編輯后的圖像時,與之綁定的編輯數(shù)據(jù)只會包含最近的編輯的細節(jié)。你可以將之前的不兼容的編輯的調(diào)整數(shù)據(jù)保存到你的輸出調(diào)整數(shù)據(jù)中,這樣你就可以為濾鏡鏈中你的階段實現(xiàn)還原功能。Photo 框架提供的還原功能將移除所有編輯,并把照片恢復到原始狀態(tài):

http://wiki.jikexueyuan.com/project/objc/images/21-19.png" alt="" />

代碼/數(shù)據(jù)共享

照片編輯擴展作為一個嵌入式二進制文件包含在容器應用中。因為蘋果要求這個容器應用必須有完整功能,因此你創(chuàng)建的照片編輯擴展,很可能與容器應用有相同的功能。你可能會希望在應用擴展和容器之間共享代碼和數(shù)據(jù)。

共享代碼通過 iOS 8 新功能 -- 創(chuàng)建 Cocoa Touch 框架 target 來實現(xiàn)。你可以向其中添加共用的功能,例如濾鏡鏈和自定義視圖類,并在應用和擴展中同時使用。

值得注意的是因為用于創(chuàng)建擴展,你必須在 Target 設置界面將 API 兼容性限制為僅擴展可用:

http://wiki.jikexueyuan.com/project/objc/images/21-20.png" alt="" />

共享數(shù)據(jù)的需求明顯要少很多,在許多情況下并不存在。然而如果需要,你可以通過把應用和擴展都添加到一個關聯(lián)到你的開發(fā)者賬號的 app group 中的方式,來創(chuàng)建一個共享容器 (shared container)。共享容器代表的是磁盤上的一塊共享的空間,你可以使用任何你喜歡的方式使用它,比如 NSUserDefaults,SQLite 或者寫文件。

調(diào)試與分析

Xcode 調(diào)試雖然有一些潛在癥結(jié),但已經(jīng)相當友好了。選擇擴展的 scheme 并編譯運行,接著會詢問你希望啟動哪一個應用,因為圖片編輯擴展只能在系統(tǒng)照片應用中實現(xiàn),所以你應該選擇照片應用:

http://wiki.jikexueyuan.com/project/objc/images/21-21.png" alt="" />

如果這么做啟動的是你的容器應用的話,你可以編輯擴展 scheme 設置 executable 為 Ask on Launch 來解決。

Xcode 然后會等待你打開你的照片編輯擴展,然后將調(diào)試器掛載上去。從這時開始,你就可以用調(diào)試標準 iOS 應用的方式來調(diào)試擴展了。將調(diào)試器附加到擴展可能需要一些時間,所以當你激活擴展時,擴展可能會失去響應一段時間。如果你想評估啟動時間的話,可以在 release 模式下運行它。

性能分析和調(diào)試類似,分析器在擴展開始執(zhí)行后附加上去。你可以更新擴展相關 scheme 指定 Xcode 詢問應該啟動哪一個應用來執(zhí)行分析。

內(nèi)存限制

擴展不是一個全功能 iOS 應用,因此訪問系統(tǒng)資源時要受到限制。更特別的是,如果用戶使用太多內(nèi)存,系統(tǒng)將優(yōu)先關閉擴展進程。我們無法確定具體的內(nèi)存限制,因為內(nèi)存管理是由 iOS 內(nèi)部處理的,但有這肯定是基于像是設備,宿主應用,以及其他應用程序的內(nèi)存壓力這些因素的。所以其實并沒有硬性的限制,但我們還是應該盡量減少內(nèi)存占用。

圖片處理是一個高內(nèi)存操作,特別是處理的對象是來自 iPhone 相機的高清晰度圖片。你需要做幾件事情來確保照片編輯擴展的內(nèi)存使用量降到最低。

  • 使用顯示尺寸的圖片 當你開始編輯進程時,系統(tǒng)提供了一張適合屏幕尺寸的圖片。用它來替代原始圖片,將在交互編輯階段將顯著減少內(nèi)存使用。
  • 限制 Core Graphics 上下文數(shù)量 雖然使用 Core Graphics 來處理圖片是正確的方式,但不要忘記 Core Graphics 上下文其實是一大塊內(nèi)存。如果你需要上下文,那么需要保持數(shù)量到最低。盡可能的重用,并想想你是否以最佳的方式在使用它。
  • 使用 GPU 無論通過 Core Image 還是類似 GPUImage 的第三方框架來用 GPU 進行處理,你可以通過鏈式調(diào)用濾鏡來降低內(nèi)存并且消除中間緩存區(qū)需求。

因為圖像編輯本身肯定就需要高內(nèi)存,所以與其他擴展相比,照片編輯擴展的可用內(nèi)存要多那么一些。在 ad hoc 測試中,圖片編輯擴展可以使用高于 100 MB 內(nèi)存。鑒于來自 800 萬像素相機的照片大約 22MB,所以這個內(nèi)存量對于大多數(shù)圖片編輯擴展來說是夠用的。

結(jié)論

iOS 8 之前,第三方開發(fā)者無法在他自己應用程序之外向用戶提供功能。擴展的出現(xiàn)徹底改變這一狀況,特別是照片編輯擴展允許你把代碼運行于照片應用核心中。盡管多次點擊的流程略顯復雜,但照片編輯擴展使用 Photo 框架的功能提供了連貫和集成的用戶體驗。

可恢復的編輯一直是像 Aperture 或 Lightroom 這樣的桌面應用的殺手級功能。而現(xiàn)在在 iOS 中使用 Photo 框架,也可以為這個功能創(chuàng)建一個通用架構(gòu)。這具有巨大的潛力,而允許第三方開發(fā)者創(chuàng)建照片編輯擴展則使這一步走得更遠。

制作照片編輯擴展方面有不少復雜的課題,但是它們都不是獨一無二的。創(chuàng)建一個直觀的用戶界面,以及設計圖像處理算法都和圖片編輯擴展一樣充滿了挑戰(zhàn)性,而它們都是一個完整的圖片編輯應用的組成部分。

目前為止有多少用戶留意到這些第三方編輯擴展還有待觀察,但總的來說這有助于提高你應用曝光率。