级别: ★★☆☆☆
标签:「iPhone app 图标」「图标生成」「启动图生成」「QiAppIconGenerator」
做者: Xs·H
审校: QiShare团队php
一个完整的app都须要多种尺寸的图标和启动图。通常状况,设计师根据开发者提供的一套规则,设计出图标和启动图供开发人员使用。但最近我利用业余时间作了个app,不但愿耽误设计师较多时间,就只要了最大尺寸的图标和启动图各一个。本想着找一下现成的工具,批量生成须要的的图片,但最后没有找到,只好使用Photoshop切出了不一样尺寸的图片。这期间,设计师还换过一次图标和启动图,我就重复了切图工做,这花费了我大量的时间。因而过后,做者开发了一个mac app——图标&启动图生成器(简称生成器)以提升工做效率。做者用两篇文章分别介绍生成器的使用和实现细节。git
接上篇文章,本篇文章介绍生成器的实现细节。github
生成器的工程很是简单,能够归纳为一个界面、一个资源文件和一个ViewController。结构以下图。算法
生成器app只有一个界面,由于界面复杂度较小,做者选用了Storyboard+Constraints的方式进行开发。下图显示了界面中的控件和约束状况。数组
其中各控件对应的类以下所示。bash
控件 | 类 |
---|---|
图片框 | NSImageView |
平台选择器 | NSComboBox |
路径按钮 | NSButton |
路径文本框 | NSTextField |
导出按钮 | NSButton |
app所支持的平台规则数据从资源文件QiConfiguration.plist
中获取。QiConfiguration.plist
至关于一个字典,每一个平台对应着字典的一对key
和value
; value
是一个数组,存储着该平台所须要的一组尺寸规格数据(item); item
是尺寸规格数据的最小单元,内部标记了该尺寸规格的图片的用途、名称和尺寸。微信
QiConfiguration.plist
的具体结构以下图所示。app
工程使用默认的ViewController
管理界面、资源数据和逻辑。 首先,界面控件元素在ViewController
中对应下图中的5个实例。工具
其中,imageView
、platformBox
和pathField
不须要响应方法。而且,platfromBox
和_pathField
的默认/记忆数据由NSUserDefaults
管理。oop
static NSString * const selectedPlatformKey = @"selectedPlatform";
static NSString * const exportedPathKey = @"exportedPath";
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
NSString *selectedPlatform = [[NSUserDefaults standardUserDefaults] objectForKey:selectedPlatformKey];
[_platformBox selectItemWithObjectValue:selectedPlatform];
NSString *lastExportedPath = [[NSUserDefaults standardUserDefaults] objectForKey:exportedPathKey];
_pathField.stringValue = lastExportedPath ?: NSHomeDirectory();
}
复制代码
这里忽略这三个控件,重点介绍pathButton
和exportButton
的响应方法中的代码逻辑。
pathButton
的响应方法负责打开文件目录,并回传选择的路径给pathField
,以显示出来。 代码以下:
- (IBAction)pathButtonClicked:(NSButton *)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.canChooseDirectories = YES;
openPanel.canChooseFiles = NO;
openPanel.title = @"选择导出目录";
[openPanel beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse result) {
if (result == NSModalResponseOK) {
self.pathField.stringValue = openPanel.URL.path;
}
}];
}
复制代码
exportButton
的响应方法负责根据imageView
中的源图片、platform
中选择的平台规则和pathField
中显示的导出路径生成图片并打开图片所在的文件夹。 代码以下:
- (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 = @"请先选择源图片、平台和导出路径";
alert.alertStyle = NSAlertStyleWarning;
[alert addButtonWithTitle:@"确认"];
[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];
}
}
复制代码
- (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 {
// 计算目标小图去贴合源大图所须要放大的比例
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;
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, toSize.width, toSize.height);
toRect = [[NSScreen mainScreen] convertRectFromBacking:toRect];
NSImage *toImage = [[NSImage alloc] initWithSize:toRect.size];
// 绘制
[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];
}
复制代码
上述是工程的全部代码,代码较多。建议有须要的同窗移步至工程源码阅读。
小编微信:可加并拉入《QiShare技术交流群》。
关注咱们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)
推荐文章:
算法小专栏:“D&C思想”与“快速排序”
iOS 避免常见崩溃(二)
算法小专栏:选择排序
iOS Runloop(一)
iOS 经常使用调试方法:LLDB命令
iOS 经常使用调试方法:断点
iOS 经常使用调试方法:静态分析
奇舞周刊