Created by Ningyuan 2020/05/23html
先扔個官方設計指南 Human Interface Guidelines:Photo Editingios
iOS 8 以後,蘋果提供了幾個應用擴展功能,分別是Today Widget
、Share
、Action
、Photo Editing
、Storage Provider
、Custom Keyboard
,豐富了iPhone的操做體驗。本次介紹的,是Photo Editing,中文譯名爲照片(圖片)編輯。objective-c
看了下網上的教程,都是比較舊的版本,因而摸索了一下,步驟以下網絡
新建一個
Single View App
項目session
新建完畢,選中項目 - TARGETS - General - 點擊 側欄下方的 "+",添加Extension項目app
向下翻,找到 Photo Editing Extension 並選中,Nextasync
在這邊填上ProductName -> Finishide
這時Xcode彈窗,是否啓用新解決方案,選擇 Activate函數
這樣就完成了Photo Editing Extension 的新建,如圖,Xcode會幫咱們新建默認文件佈局
先看一下info.plist文件,能夠看到新增了Key-Value:NSExtension,展開其全部子項
PHSupportedMediaTypes:支持編輯的類型,默認爲Image類型,還能夠添加
NSExtensionMainStroyboard:stroyboard名稱
NSExtensionPointIdentifier:照片編輯擴展,標識,不需更改
MainInterface.storyboard,這個是照片擴展的主界面,已經自動生成了"Hello World",Run Target中應該會自動選中咱們當前的擴展應用,運行時選中要調起擴展的App,這裏選擇Photos。
啓動相冊後,隨便點開一張照片,而後點擊右上角"編輯"。
後續在安裝本體應用後,也能夠直接在圖庫中選中編輯,進入咱們的擴展應用。
接下來會進入系統照片編輯界面,在右上角找到更多按鈕"···"(系統不一樣,所在位置不一樣),點擊
在展開的界面中,能夠看到當前可用的擴展應用,PhotoExtensionTest、extension我是以前新建的照片編輯擴展應用,Ex則是剛剛新建的。
若是在這裏沒看到,可點擊"更多"查看。
隨後在彈出的界面中的建議這裏選擇便可。
而後咱們選中剛剛的Ex,就打開了擴展應用。
打開PhotoEditingViewController.m,能夠看到系統自動生成以下代碼,界面佈局咱們能夠直接在MainInterface.storyboard中直接佈局,也能夠經過代碼的形式佈局。
#import "PhotoEditingViewController.h"
#import <Photos/Photos.h>
#import <PhotosUI/PhotosUI.h>
@interface PhotoEditingViewController () <PHContentEditingController>
@property (strong) PHContentEditingInput *input;
@end
@implementation PhotoEditingViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark - PHContentEditingController
- (BOOL)canHandleAdjustmentData:(PHAdjustmentData *)adjustmentData {
// Inspect the adjustmentData to determine whether your extension can work with past edits.
// (Typically, you use its formatIdentifier and formatVersion properties to do this.)
return NO;
}
- (void)startContentEditingWithInput:(PHContentEditingInput *)contentEditingInput placeholderImage:(UIImage *)placeholderImage {
// Present content for editing, and keep the contentEditingInput for use when closing the edit session.
// If you returned YES from canHandleAdjustmentData:, contentEditingInput has the original image and adjustment data.
// If you returned NO, the contentEditingInput has past edits "baked in".
self.input = contentEditingInput;
}
- (void)finishContentEditingWithCompletionHandler:(void (^)(PHContentEditingOutput *))completionHandler {
// Update UI to reflect that editing has finished and output is being rendered.
// Render and provide output on a background queue.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
// Create editing output from the editing input.
PHContentEditingOutput *output = [[PHContentEditingOutput alloc] initWithContentEditingInput:self.input];
// Provide new adjustments and render output to given location.
// output.adjustmentData = <#new adjustment data#>;
// NSData *renderedJPEGData = <#output JPEG#>;
// [renderedJPEGData writeToURL:output.renderedContentURL atomically:YES];
// Call completion handler to commit edit to Photos.
completionHandler(output);
// Clean up temporary files, etc.
});
}
- (BOOL)shouldShowCancelConfirmation {
// Returns whether a confirmation to discard changes should be shown to the user on cancel.
// (Typically, you should return YES if there are any unsaved changes.)
return NO;
}
- (void)cancelContentEditing {
// Clean up temporary files, etc.
// May be called after finishContentEditingWithCompletionHandler: while you prepare output.
}
複製代碼
寫代碼以前,先簡單講一下幾個重要方法。
2.1 startContentEditingWithInput: placeholderImage
viewDidLoad以後,會先走這個函數,拿到系統傳進來的contentEditingInput,包含了外面傳進來的圖片原數據,通常能夠在這邊進行圖片顯示操做。
- (void)startContentEditingWithInput:(PHContentEditingInput *)contentEditingInput placeholderImage:(UIImage *)placeholderImage {
// Present content for editing, and keep the contentEditingInput for use when closing the edit session.
// If you returned YES from canHandleAdjustmentData:, contentEditingInput has the original image and adjustment data.
// If you returned NO, the contentEditingInput has past edits "baked in".
self.input = contentEditingInput;
}
複製代碼
2.2 finishContentEditingWithCompletionHandler:
下面這個函數,在點擊界面右上角完成
按鈕時觸發,將對圖片修改好的數據,經過block的形式回調。
- (void)finishContentEditingWithCompletionHandler:(void (^)(PHContentEditingOutput *))completionHandler {
// Update UI to reflect that editing has finished and output is being rendered.
// Render and provide output on a background queue.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
// 根據輸入元數據input,建立輸出數據output
PHContentEditingOutput *output = [[PHContentEditingOutput alloc] initWithContentEditingInput:self.input];
// Provide new adjustments and render output to given location.
// output.adjustmentData = <#new adjustment data#>;
// NSData *renderedJPEGData = <#output JPEG#>;
// [renderedJPEGData writeToURL:output.renderedContentURL atomically:YES];
// Call completion handler to commit edit to Photos.
completionHandler(output);
// Clean up temporary files, etc.
});
}
複製代碼
2.3 shouldShowCancelConfirmation
點擊取消按鈕的時候,會調用此方法。
- (BOOL)shouldShowCancelConfirmation {
// Returns whether a confirmation to discard changes should be shown to the user on cancel.
// (Typically, you should return YES if there are any unsaved changes.)
return NO;
}
複製代碼
2.4 canHandleAdjustmentData:
是否使用已修改過的數據。
- (BOOL)canHandleAdjustmentData:(PHAdjustmentData *)adjustmentData {
// Inspect the adjustmentData to determine whether your extension can work with past edits.
// (Typically, you use its formatIdentifier and formatVersion properties to do this.)
return NO;
}
複製代碼
2.5 cancelContentEditing
會在 finishContentEditingWithCompletionHandler: 以後進行調用,通常作一些清理臨時文件等工做。
- (void)cancelContentEditing {
// Clean up temporary files, etc.
// May be called after finishContentEditingWithCompletionHandler: while you prepare output.
}
複製代碼
其餘一些自定義佈局代碼就不放出來了,這裏仍是講一下主要功能代碼,其餘的網絡上也有很多,可自行學習查看。
進入獲取input、placeholderImage圖片
- (void)startContentEditingWithInput:(PHContentEditingInput *)contentEditingInput placeholderImage:(UIImage *)placeholderImage {
// Input輸入
self.input = contentEditingInput;
// 獲取選擇圖片
self.originalImage = placeholderImage;
// 本身處理圖片佈局之類的
[self updateImageViewWithImage:placeholderImage];
}
複製代碼
取消編輯的提示。
- (BOOL)shouldShowCancelConfirmation {
// iconImageView是我定義的一個添加到原始圖片上的imageView
// 這裏的條件本身設置,我這邊只是簡單地判斷iconImagView.image是否爲空
// 不爲空則說明已經對圖片進行了修改,return YES,則告訴系統須要彈窗提示
if (self.iconImageView.image != nil) {
return YES;
}
return NO;
}
複製代碼
編輯圖片完成,將圖片數據傳出去。
- (void)finishContentEditingWithCompletionHandler:(void (^)(PHContentEditingOutput *))completionHandler {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
PHContentEditingOutput *output = [[PHContentEditingOutput alloc] initWithContentEditingInput:self.input];
// 輸出圖片數據
NSData *imageData = UIImageJPEGRepresentation([self snapShotWithView:self.imageView], 1.0);
// 爲圖片添加標識符跟版本號,下次進來的時候,會觸發canHandleAdjustmentData:方法,能夠拿到本次寫入的數據,來判斷是否使用該圖片做爲基礎進行二次編輯
PHAdjustmentData *adjustmentData = [[PHAdjustmentData alloc] initWithFormatIdentifier:@"com.PhotoExtensionTest" formatVersion:@"1.0" data:imageData];
output.adjustmentData = adjustmentData;
NSData *renderedJPEGData = imageData;
[renderedJPEGData writeToURL:output.renderedContentURL atomically:YES];
// 回調,確認當前修改
completionHandler(output);
});
}
/// 截圖
- (UIImage *)snapShotWithView:(UIView *)view {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
複製代碼
canHandleAdjustmentData: 拿到標識符,使用已編輯的圖片數據,繼續二次編輯
- (BOOL)canHandleAdjustmentData:(PHAdjustmentData *)adjustmentData {
NSString *formatIdentifier = adjustmentData.formatIdentifier;
NSString *formatVersion = adjustmentData.formatVersion;
NSData *storageData = adjustmentData.data;
UIImage *image = [UIImage imageWithData:storageData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 0, 100, 100 * (image.size.height / image.size.width));
// 拿到存儲的標識符與版本號,則return YES
NSLog(@"formatIdentifier:%@ - formatVersion:%@", formatIdentifier, formatVersion);
if (formatIdentifier && formatVersion) {
return YES;
}
// return NO表示不取adjustmentData裏的已編輯數據,始終取原數據
return NO;
}
複製代碼
編輯界面以下,在底部選擇一個小icon,添加至修改圖片。
點擊完成後,會將數據傳到系統圖片編輯界面,再次點擊完成,則會自動返回相冊,能夠看到修改爲功。
此時再對已修改的圖片點擊編輯,能夠看到系統編輯界面右下角顯示"復原",點擊則會恢復成本來的圖片。
咱們在這裏再次右上角的更多按鈕,打開咱們的擴展應用。
此時能夠看到,啓動擴展應用後,點擊底部的icon就又添加了一個icon在圖片上,這是由於代碼中設置的canHandleAdjustmentData: 生效的結果。