开发中一直在使用 cocoapods
, git
, class-dump
, cycript
等强大的命令行工具.
今天就来自己尝试做一个简单了解一下这些工具的工作原理.
开始前先讲个故事:
UI :
iOS 的 icon 都需要那些尺寸 ◔ ‸◔??
我 :给我一张 1024 的就好 (-_-#). (思路被打断正不爽)
.
于是后来每次我都只能拿到一张 1024 * 1024
的 icon, 不得不每次都自己用 预览
来导出各种尺寸的 icon.
毕竟自作自受 ("▔□▔)/
, 那今天就写个生成各种尺寸 icon 的工具吧.
0x00 如何开始
-
首先使用 Xcode 创建一个
Command Line
工程. 没错, 就是那种只有一个.m
文件,run
了之后只会printf
和NSLog
的那种工程, 我的项目名称就叫appicon
, 后面会提到. - 导入头文件
#import <Cocoa/Cocoa.h>
, 因为是基于 macOS 的, 所以导入这个. 不然NSFileManager
都用不了, 大概… - 先上代码, 下面再解释.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *input = nil;
NSString *output = nil;
int ch;
// 读取参数
while ((ch = getopt(argc, argv, "i:o:")) != -1) {
switch (ch) {
case 'o':
output = [NSString stringWithUTF8String:optarg];
break;
case 'i':
input = [NSString stringWithUTF8String:optarg];
break;
}
}
BOOL isDir = NO;
// 判断文件路径, 以及输出目标路径
if (input && output && [[NSFileManager defaultManager] fileExistsAtPath:output isDirectory:&isDir]) {
NSImage *image = [[NSImage alloc] initWithContentsOfFile:input];
if (image == nil) {
printf("File Load Failed At %s", input.UTF8String);
exit(0);
}
if (!isDir) {
output = [output stringByAppendingPathComponent:@"icons"];
}
writeImage(image, output, 180);
writeImage(image, output, 120);
writeImage(image, output, 80);
writeImage(image, output, 60);
writeImage(image, output, 40);
} else {
printf("Path Error!\n");
}
}
return 0;
}
也没多少代码, 看起来也没多难, 应该都能看懂, 除了这段:
1
2
3
4
5
6
7
8
9
10
11
int ch;
while ((ch = getopt(argc, argv, "i:o:")) != -1) {
switch (ch) {
case 'o':
output = [NSString stringWithUTF8String:optarg];
break;
case 'i':
input = [NSString stringWithUTF8String:optarg];
break;
}
}
首先要注意一个函数 getopt
, 这个就是获取命令行输入参数的关键.
1
2
// 函数返回 -1 表示参数解析完毕
int getopt(int argc, char * const argv[], const char *optstring);
看函数声明有没有熟悉的东西?
我们的main
函数 int main(int argc, const char * argv[])
里面有一模一样的 2 个参数.
argc:
main()
函数传递过来的参数的个数 argv:main()
函数传递过来的参数的字符串指针数组 optstring:选项字符串,告知getopt()
可以处理哪个选项以及哪个选项需要参数
argc
和 argv
直接放进去就可以了, optstring
此处传入的是 "i:o:"
.
char* optstring = "ab:c::";
| 字符 | 意义 | 格式 |
| —————— | ————————- | ———————————– |
| 单个字符 a | 表示选项a没有参数 | 格式:-a 即可,不加参数 |
| 单字符加冒号 b: | 表示选项b有且必须加参数 | 格式:-b 100 或 -b100 ,但 -b=100 错 |
| 单字符加2冒号 c: : | 表示选项c可以有,也可以无 | 格式:-c200,其它格式错误 |
因此此处的 "i:o:"
, 表示有 2 个参数 -i
和 -o
, 分别代表 input
和 output
, 可以看成是参数名.
使用一个while
循环来获取所有的参数, 此处的 optarg
就是获取后的参数值.
以下写过 iOS 的稍微转换一下就可以了.都是常见的功能. 压缩图片尺寸:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
压缩图片到指定尺寸
@param image 图片对象
@param width 目标尺寸
@return 压缩结果
*/
NSData * compressImage(NSImage *image, float width) {
NSData *data = image.TIFFRepresentation;
if (data == nil) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)data, NULL);
CGImageRef cgimage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:cgimage];
rep.size = CGSizeMake(width, width);
NSData *pngData = [rep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
return pngData;
}
把 image 写入指定的位置.
1
2
3
4
5
6
7
8
9
10
11
/**
处理图片并把结果写入磁盘
@param image 被处理的图片对象
@param path 写入路径
@param size 目标图片尺寸
*/
void writeImage(NSImage *image, NSString *path, float size) {
path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%.0f.png", size]];
[compressImage(image, size) writeToFile:path atomically:NO];
}
0x01 如何使用
- 项目编译之后会生成一个
appicon
,Product
文件夹下的Mach-O
文件. - 复制
appicon
到usr/local/bin
, 放在此目录下, 在任何文件夹下都能够找到命令, 不然还要指定路径. - 使用命令:
1
$ appicon -i [输入文件路径] -o [输出目录路径]
例如:
1
$ appicon -i ~/Desktop/1024.png -o ~/Desktop
或者
1
2
$ cd ~/Desktop/
$ appicon -i ./1024.png -o ./
而上文中 input
和 output
分别就对应 ~/Desktop/1024.png
和 ~/Desktop
.