Painted painted levels: fostered fostered fostered

Tags: “iPhone App icon” “Icon Generation” “Startup diagram generation” “QiAppIconGenerator


A complete app requires multiple sizes of ICONS and launch images. Typically, designers design ICONS and launch diagrams for developers to use based on a set of rules provided by the developer. However, RECENTLY I made an app in my spare time, so as not to waste too much time of the designer, I only need an icon of the largest size and a startup picture. I wanted to find a ready-made tool to batch generate the required pictures, but failed to find it in the end, so I had to use Photoshop to cut out pictures of different sizes. During this time, the designer also changed the icon and the splash image once, and I repeated the cutting image, which took a lot of my time. Therefore, the author developed a MAC app — Icon & Startup Graph Generator (Generator for short) to improve work efficiency. The author introduces the use and implementation details of generators in two articles.

Following the previous article, this article covers the implementation details of the generator.

The generator project is very simple and can be summarized as an interface, a resource file, and a ViewController. The structure is shown below.

A, interface

The generator app has only one interface, and because the interface is less complex, the author chose to use Storyboard+Constraints for development. The following figure shows the controls and constraints in the interface.

The corresponding classes of each control are as follows.

controls class
Picture box NSImageView
Platform selector NSComboBox
The path button NSButton
Path text field NSTextField
The export button NSButton

Ii. Resource files

The platform rule data supported by the app is obtained from the resource file QiConfiguration. Plist. QiConfiguration. Plist is a dictionary with a pair of keys and values for each platform; Value is an array that stores a group of dimension data (item) required by the platform. Item is the smallest unit of dimension specification data. It contains the purpose, name, and size of the picture marked with the dimension.

The structure of QiConfiguration. Plist is shown below.

Third, ViewController

The project uses the default ViewController to manage the interface, resource data, and logic. First, the interface control elements correspond to the five instances in the ViewController shown in the figure below.

Among them, imageView, platformBox, and pathField do not require response methods. Also, the default/memory data for platfromBox and _pathField is managed by NSUserDefaults.

static NSString * const selectedPlatformKey = @"selectedPlatform";
static NSString * const exportedPathKey = @"exportedPath";
Copy the code
- (void)viewDidLoad { [super viewDidLoad]; NSString *selectedPlatform = [[NSUserDefaults standardUserDefaults] objectForKey:selectedPlatformKey]; [_platformBox selectItemWithObjectValue:selectedPlatform]; NSString *lastExportedPath = [[NSUserDefaults standardUserDefaults] objectForKey:exportedPathKey]; _pathField.stringValue = lastExportedPath ? : NSHomeDirectory(); }Copy the code

These three controls are ignored here and the focus is on the code logic in the response methods of pathButton and exportButton.

1. – pathButtonClicked:

PathButton’s response method is responsible for opening the file directory and passing back the selected path to pathField for display. The code is as follows:

- (IBAction)pathButtonClicked:(NSButton *)sender { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; openPanel.canChooseDirectories = YES; openPanel.canChooseFiles = NO; Openpanel. title = @" Select export directory "; [openPanel beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse result) { if (result == NSModalResponseOK) { self.pathField.stringValue = openPanel.URL.path; } }]; }Copy the code
2. – exportButtonClicked:

The response method of exportButton is responsible for generating an image and opening the folder where the image is located based on the source image in imageView, the platform rules selected in Platform, and the export path shown in pathField. The code is as follows:

- (IBAction)exportButtonClicked:(NSButton *)sender { NSImage *image = _imageView.image; NSString *platform = _platformBox.selectedCell.title; NSString *exportPath = _pathField.stringValue; if (! image || ! platform || ! exportPath) { NSAlert *alert = [[NSAlert alloc] init]; Alert. MessageText = @" Please select source image, platform and export path first "; alert.alertStyle = NSAlertStyleWarning; [alert addButtonWithTitle:@" confirm "]; [alert beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse returnCode) {}]; } else { [[NSUserDefaults standardUserDefaults] setObject:platform forKey:selectedPlatformKey]; [[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] setObject:exportPath forKey:exportedPathKey]; [[NSUserDefaults standardUserDefaults] synchronize]; [self generateImagesForPlatform:platform fromOriginalImage:image]; }}Copy the code
- (void)generateImagesForPlatform:(NSString *)platform fromOriginalImage:(NSImage *)originalImage { NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"QiConfiguration" ofType:@"plist"]; NSDictionary *configuration = [NSDictionary dictionaryWithContentsOfFile:plistPath]; NSArray<NSDictionary *> *items = configuration[platform]; NSString *directoryPath = [[_pathField.stringValue stringByAppendingPathComponent:platform] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:nil]; if ([platform containsString:@"AppIcons"]) { [self generateAppIconsWithConfigurations:items fromOriginalImage:originalImage toDirectoryPath:directoryPath]; } else if ([platform containsString:@"LaunchImages"]) { [self generateLaunchImagesWithConfigurations:items fromOriginalImage:originalImage toDirectoryPath:directoryPath]; } } - (void)generateAppIconsWithConfigurations:(NSArray<NSDictionary *> *)configurations fromOriginalImage:(NSImage *)originalImage toDirectoryPath:(NSString *)directoryPath { for (NSDictionary *configuration in configurations) { NSImage *appIcon = [self generateAppIconWithImage:originalImage forSize:NSSizeFromString(configuration[@"size"])]; NSString *filePath = [NSString stringWithFormat:@"%@/%@.png", directoryPath, configuration[@"name"]]; [self exportImage:appIcon toPath:filePath]; } [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:directoryPath isDirectory:YES]]; } - (void)generateLaunchImagesWithConfigurations:(NSArray<NSDictionary *> *)configurations fromOriginalImage:(NSImage *)originalImage toDirectoryPath:(NSString *)directoryPath { for (NSDictionary *configuration in configurations) { NSImage *launchImage = [self generateLaunchImageWithImage:originalImage forSize: NSSizeFromString(configuration[@"size"])]; NSString *filePath = [NSString stringWithFormat:@"%@/%@.png", directoryPath, configuration[@"name"]]; [self exportImage:launchImage toPath:filePath]; } [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:directoryPath isDirectory:YES]]; } - (NSImage *)generateAppIconWithImage:(NSImage *)fromImage forSize:(CGSize)toSize { NSRect toFrame = NSMakeRect(.0, .0, toSize.width, toSize.height); toFrame = [[NSScreen mainScreen] convertRectFromBacking:toFrame]; NSImageRep *imageRep = [fromImage bestRepresentationForRect:toFrame context:nil hints:nil]; NSImage *toImage = [[NSImage alloc] initWithSize:toFrame.size]; [toImage lockFocus]; [imageRep drawInRect:toFrame]; [toImage unlockFocus]; return toImage; } - (NSImage *) generateLaunchImageWithImage: (NSImage *) fromImage forSize: CGSize toSize {/ / calculation target insets to enlarge the proportion of joint source large need CGFloat wFactor = fromImage.size.width / toSize.width; CGFloat hFactor = fromImage.size.height / toSize.height; CGFloat toFactor = fminf(wFactor, hFactor); Rect CGFloat scaledWidth = tosize.width * toFactor; Rect CGFloat scaledWidth = tosize.width * toFactor; CGFloat scaledHeight = toSize.height * toFactor; CGFloat scaledOriginX = (fromImage.size.width - scaledWidth) / 2; CGFloat scaledOriginY = (fromImage.size.height - scaledHeight) / 2; NSRect fromRect = NSMakeRect(scaledOriginX, scaledOriginY, scaledWidth, scaledHeight); Rect NSRect toRect = NSMakeRect(.0,.0, tosie.width, tosie.height); toRect = [[NSScreen mainScreen] convertRectFromBacking:toRect]; NSImage *toImage = [[NSImage alloc] initWithSize:toRect.size]; // Draw [toImage lockFocus]; [fromImage drawInRect: toRect fromRect: fromRect operation: NSCompositeCopy fraction: 1.0]; [toImage unlockFocus]; return toImage; } - (void)exportImage:(NSImage *)image toPath:(NSString *)path { NSData *imageData = image.TIFFRepresentation; NSData *exportData = [[NSBitmapImageRep imageRepWithData:imageData] representationUsingType:NSPNGFileType properties:@{}]; [exportData writeToFile:path atomically:YES]; }Copy the code

The above is all the code of the project, the code is more. It is recommended that students who need to move to the project source code to read.


Recommended articles:

IOS Runloop (a) iOS common debugging method: LLDB command iOS common debugging method: breakpoint iOS common debugging method: static analysis odd dance weekly