Created by Ningyuan 2020/05/23

Human Interface Guidelines:Photo Editing

After iOS 8, Apple provided several app extensions, including Today Widget, Share, Action, Photo Editing, Storage Provider, and Custom Keyboard, to enrich the iPhone operation experience. This is the Photo Editing.

1. New

I read the online tutorials, which are all older versions, so I fumbled with them. The steps are as follows

Create a new Single View App project

[[email protected]

After creating the Extension project, select project -targets -general – click the “+” at the bottom of the sidebar to add the Extension project

[[email protected]

Scroll down to Photo Editing Extension and select Next

[[email protected]

Put ProductName here -> Finish

[[email protected]

When Xcode pops up, select Activate to enable the new solution

[[email protected]

This completes the creation of the Photo Editing Extension, as shown here, and Xcode will create the default file for us

[[email protected]

If you look at the info.plist file, you can see that key-value: NSExtension has been added to expand all of its children

PHSupportedMediaTypes: Type that supports editing, default is Image, and can be added

NSExtensionMainStroyboard: stroyboard name

NSExtensionPointIdentifier: photo editing extension, logo, do not need to change

[[email protected]

Maininterface. storyboard, which is the MainInterface for the photo extension, has already automatically generated “Hello World”, and the Run Target should automatically select our current extension App, and at Run time select the App that you want to call up the extension, so Photos.

[[email protected]

After launching the album, click on a random photo and click “Edit” in the upper right corner.

Later, after installing ontology application, you can also directly select and edit in the gallery to enter our extended application.

[[email protected]

Next, enter the system photo editing interface, find more buttons “···” in the upper right corner (different system, different location), click

[[email protected]

In the expanded screen, you can see the currently available extensions, PhotoExtensionTest, Extension I is a newly created photo editing extension, and Ex is newly created.

If you don’t see it here, click “More” to see it.

[[email protected]

Then select the suggestion in the pop-up interface.

[[email protected]

Then we select Ex, and we open the extension application.

[[email protected]

2. Code understanding

Open PhotoEditingViewController. J m, you can see the system automatically generate the following code, interface layout. We can directly in MainInterface layout directly in the storyboard, can also use the code in the form of a layout.

#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. }Copy the code

Before we write the code, let’s just talk about a few important methods.

2.1 startContentEditingWithInput: placeholderImage

After viewDidLoad, this function is used first to get the contentEditingInput from the system, which contains raw image data from the outside and is generally used for image display.

- (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;
}
Copy the code

2.2 finishContentEditingWithCompletionHandler:

The following function, triggered when the finish button is clicked in the upper right corner of the screen, calls back the modified data in the form of a 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), ^{ Create output data output PHContentEditingOutput * output = [[PHContentEditingOutput alloc] initWithContentEditingInput: self. The 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. }); }Copy the code

2.3 shouldShowCancelConfirmation

This method is called when the Cancel button is clicked.

- (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;
}
Copy the code

2.4 canHandleAdjustmentData:

Whether to use modified data.

- (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;
}
Copy the code

2.5 cancelContentEditing

Will be invoked after finishContentEditingWithCompletionHandler:, generally do some clean up the temporary files, etc.

- (void)cancelContentEditing {
    // Clean up temporary files, etc.
    // May be called after finishContentEditingWithCompletionHandler: while you prepare output.
}
Copy the code

3. Code implementation

Some other custom layout code will not be put out, here or talk about the main function code, other network also have many, can learn to view.

Obtain input and placeholderImage

- (void)startContentEditingWithInput:(PHContentEditingInput *)contentEditingInput placeholderImage:(UIImage *)placeholderImage {// Input Input self. Input = contentEditingInput; Self. OriginalImage = placeholderImage; / / such as their processing photo layout [self updateImageViewWithImage: placeholderImage]; }Copy the code

Cancel edit prompt.

- (BOOL) shouldShowCancelConfirmation {/ / iconImageView is my definition of a is added to the original image on the imageView yourself / / conditions here, Iconimageview.image = iconimageview.image = iconimageview.image = iconimageview.image = iconimageview.image = iconimageview.image = iconimageview.image! = nil) { return YES; } return NO; }Copy the code

After editing the image, send out the image data.

- (void)finishContentEditingWithCompletionHandler:(void (^)(PHContentEditingOutput *))completionHandler { dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ PHContentEditingOutput *output = [[PHContentEditingOutput alloc] initWithContentEditingInput:self.input]; / / output image data NSData * imageData = UIImageJPEGRepresentation ([self snapShotWithView: self. ImageView], 1.0); // Add an identifier and version number to the image. The next time it comes in, the canHandleAdjustmentData: method is triggered to retrieve the data that was written to the image. AdjustmentData = [[PHAdjustmentData alloc] InitWithFormatIdentifier: @ "com. PhotoExtensionTest formatVersion:" @ "1.0" data: imageData]; output.adjustmentData = adjustmentData; NSData *renderedJPEGData = imageData; [renderedJPEGData writeToURL:output.renderedContentURL atomically:YES]; // Callback to confirm the current modification completionHandler(output); }); } / / / screenshot - (UIImage *) snapShotWithView (UIView *) view {UIGraphicsBeginImageContextWithOptions (view. Bounds. The size, YES, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); [view.layer renderInContext:context]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }Copy the code

CanHandleAdjustmentData: Get the identifier, use the edited image data, and continue the second edit

- (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; } public adjustmentData (adjustmentData, adjustmentData, adjustmentData, adjustmentData, adjustmentData) }Copy the code

Effect of 4.

The edit interface is as follows. Select a small icon at the bottom and add it to the modified picture.

[[email protected]

After clicking “Finish”, the data will be transferred to the picture editing interface of the system. Click “Finish” again, the album will be returned automatically, and you can see that the modification is successful.

[[email protected]

At this time, click Edit on the modified picture, you can see “Restore” displayed in the lower right corner of the editing interface of the system, click to restore the original picture.

We’re here again with more buttons in the upper right corner to open our extension app.

[[email protected]

At this point, you can see that after starting the extension application, clicking the icon at the bottom adds another icon on the image, because the code setting canHandleAdjustmentData: takes effect.

[[email protected]

5. Summary

  • Photo Editing Extension can only be started by the system App, Photos
  • This extension allows you to modify photos and restore them without worrying about the original data being affected
  • After the photo is modified, the identifier and version can be set, which makes the subsequent editing of the photo more flexible

References:

  • Developer.apple.com/library/arc…

  • www.jianshu.com/p/699b14e3f…