iOS 在系统相册调用自己的应用编辑图片 - Photo Editing Extension
2020/5/25 23:27:13
本文主要是介绍iOS 在系统相册调用自己的应用编辑图片 - Photo Editing Extension,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Created by Ningyuan 2020/05/23
先扔个官方设计指南 Human Interface Guidelines:Photo Editing
iOS 8 之后,苹果提供了几个应用扩展功能,分别是Today Widget
、Share
、Action
、Photo Editing
、Storage Provider
、Custom Keyboard
,丰富了iPhone的操作体验。本次介绍的,是Photo Editing,中文译名为照片(图片)编辑。
1. 新建
看了下网上的教程,都是比较旧的版本,于是摸索了一下,步骤如下
新建一个
Single View App
项目
新建完毕,选中项目 - TARGETS - General - 点击 侧栏下方的 "+",添加Extension项目
向下翻,找到 Photo Editing Extension 并选中,Next
在这边填上ProductName -> Finish
这时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,就打开了扩展应用。
2. 代码了解
打开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. } 复制代码
3. 代码实现
其他一些自定义布局代码就不放出来了,这里还是讲一下主要功能代码,其他的网络上也有不少,可自行学习查看。
进入获取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; } 复制代码
4. 效果
编辑界面如下,在底部选择一个小icon,添加至修改图片。
点击完成后,会将数据传到系统图片编辑界面,再次点击完成,则会自动返回相册,可以看到修改成功。
此时再对已修改的图片点击编辑,可以看到系统编辑界面右下角显示"复原",点击则会恢复成原本的图片。
我们在这里再次右上角的更多按钮,打开我们的扩展应用。
此时可以看到,启动扩展应用后,点击底部的icon就又添加了一个icon在图片上,这是因为代码中设置的canHandleAdjustmentData: 生效的结果。
5. 小结
- Photo Editing Extension 只能由系统App,Photos启动
- 该拓展可以实现对照片的修改,并且系统可将其复原,不用担心原数据被影响
- 照片修改后可设置标识符与版本,使得在后续对照片的编辑上更灵活
参考资料:
-
developer.apple.com/library/arc…
-
www.jianshu.com/p/699b14e3f…
这篇关于iOS 在系统相册调用自己的应用编辑图片 - Photo Editing Extension的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值