什么是Iconfont
我们通常看到的图标都是以图片形式集成到项目中使用,而 Iconfont 是一套字体图标,和我们使用自定义字体的方式是一样的,并且它是一种矢量图标。
计算机中显示的图形一般分为两类---位图和矢量图,我们平常使用的JPEG、PNG等图片都是位图格式,是一种由像素来表示的图像,而矢量图是由点、直线、多边形等基于数学方程的几何图元表示的图像,对比位图,矢量图具有体积小,放大缩小都不会失真的优点,这个优点就可以给项目带来很大好处了,但缺点是无法用来表达色彩层次丰富的图像,因此一些色彩复杂的图形仍然需要位图去表达。正巧我们项目在进行模块化,碰到不同模块使用到相同的图片时,尤其是这种基础icon,复制多份到各自的模块中是不太优雅的,利用Iconfont就可以很好的解决。
优点
- 缩放不会模糊,告别iOS中2x/3x以及未来nx的问题
- 一套资源可在web、iOS、Android等多个平台使用
- 一键换肤、方便更改图片颜色,图片复用
- 一定程度上减小包体积
- 有利于项目模块化
缺点
- 图标制作/更新成本高
- 只支持单色
如何制作Iconfont
首先,你得拥有一套完美的图标库,如下图,这里我们选用Sketch为容器去维护这些图标,毕竟对程序员来说是比较容易上手的工具,注意矢量图的制作还是需要设计师的哦,不然有的是坑让你踩的。不管使用AI还是其他什么矢量图绘制软件制作的图标我们最终都放入这个文件中,后续的更新也是往后面逐一增加。
可以看出来,其实都是一些基础通用icon,这些icon复用率非常高,如果你们公司有多个App,复用这一个字体文件就可以快速使用图标而不需要各种拷贝图片资源了。我们最终需要的是一个包含所有图标的字体文件即ttf文件,使用过自定义字体的开发者应该都知道ttf(TrueType font),我们在电脑上是可以直接双击ttf文件安装字体使用的。如下图
那么如何将这些矢量图标最终合并为一个字体文件呢?
将sketch中的图标导出
上面说了有矢量图和位图之分,那么位图就是平常我们导出的后缀为png、jpeg、gif这种格式的,矢量图一般有.ai,.pdf,.svg等等,这里我们导出为svg格式。Bohemian Coding(Sketch的制作团队)发布过一款名为 SketchTool 的命令行工具,用来自动导出 .sketch 文件当中的界面和切片。如果你愿意手动一张张导出也可以,我敬你是条汉子~
安装好 sketch 后,在命令行中执行如下命令安装 sketchtool (换成你自己的路径)
sh /Applications/Sketch.app/Contents/Resources/sketchtool/install.sh
我们通过下面的命令批量导出图标,其中 ${SRCROOT}/../ZanIconFont/Icon/icons.sketch 替换成你的 iconfont.sketch 的路径,${SRCROOT}/../ZanIconFont/Icon/svg 替换成你的导出目标路径
sketchtool export slices ${SRCROOT}/../ZanIconFont/Icon/icons.sketch --output=${SRCROOT}/../ZanIconFont/Icon/svg --formats=svg
export slices :导出切片,这个跟sketch中的设置相关,我们这里每一个图标设置成了slice,所以对应的命令使用的是 export slices,如果每个图标都有自己的artboard,那么就是 export artboards。
--format=svg :导出为svg格式。
sketchtool相关的命令以及参数我们都可以通过在命令行中输入sketchtool来查看,这里就不细说啦。导出的部分svg图片如下:
合成ttf字体文件
我们需要将上述所有svg图片合并为一个ttf,这里我们使用的是内部前端组开发的一个命令行工具 iconfount,已经开源,安装以及详细说明可以在github上看到。
在命令行中执行
iconfount --found-config.js
其中 found-config.js 是你的配置文件,支持js或者json,在iconfount项目的sample/目录下有个示例配置文件,可以参考。看下我们的配置文件部分截图:
name:字体名称(familyName),就是后续代码中注册字体用到的。
output:输出字体、样式以及示例文件的目录,可以是相对路径或者绝对路径
glyphs_dir:存放svg文件的根目录,就是上一步生成的svg目录。
glyphs:所有图标的定义,每个图标都有keywords、src等等若干其他属性,我们这里只需要使用src属性即可。最好与项目中每个图标的name保持一致。
其他的参数在github上都有详细说明,这里就不一一列举啦。
最终会生成如下一些文件
可以看到在font文件夹中已经包含了ttf,另外还有woff、eot等其他类型,这些都是用于web端的,对于App可以先不用管。我们再看到有个demo.html如下,列举了图标列表,点击右上角show codes可以查看对应的编码。我们在项目中使用iconfont时是需要依赖这个demo.html的可视化界面的,否则你不知道编码所对应的图标是长什么样子的。
以上是生成ttf字体文件的整个流程,而另外也有很多优秀的平台提供整套功能,例如 Iconfont,icomoon,fontello 等等,这些平台都提供了很多成熟的图标集,支持在线导入自定义的SVG图标,生成样式、字体文件等等一整套方案,关于这些平台的使用这里不再赘述,各个官网以及很多文章都有很详细的说明。但是我们考虑到的是后期维护更新还是比较麻烦,如果更新图标需要重新导入到平台上、生成字体文件、再引入项目,每次需要手动去完成。因此产生了 iconfount,他是基于fontello、使用了很多fontello的代码和库而开发一个命令行工具,能够很好的整合到项目中完成自动化,后续图标更新了,设计师只要更新图标本身即可,而我们的Iconfont库重新build一遍即可完成所有的操作。这里iOS使用了Cocoapods去管理Iconfont私有库,因此每次更新图标后Iconfont库的开发者去更新下sketch整个文件,重新build一遍就会自动去执行导出svg图标、生成字体文件的脚本,业务方升级一下版本即可。
在iOS中使用
先看下简单的demo
首先把上面制作的ttf字体文件引入到项目中,代码中注册字体,打印出来是可以找到你的字体的。
+ (void)registerFontWithURL:(NSURL *)url {
NSAssert([[NSFileManager defaultManager] fileExistsAtPath:[url path]], @"Font file doesn't exist");
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)url);
CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider);
CGDataProviderRelease(fontDataProvider);
CTFontManagerRegisterGraphicsFont(newFont, nil);
CGFontRelease(newFont);
NSLog(@"%@",[UIFont familyNames]);
}
使用场景:
as text
是字体文件的一般用法,代码如下。但是这种方式在替换原来的图标过程中会改变添加控件的方式,原本都是用UIImageView图片控件而现在要改成UILabel或其他文本控件了,所以这边我们推荐使用第二种。
label.attributedText = [ZanIconFont attributedStringWithIcon:zicon_edit fontSize:20 color:[UIColor redColor]]
部分实现
+ (NSAttributedString *)attributedStringWithIcon:(ZanIconName)iconName fontSize:(CGFloat)fontSize color:(UIColor *)color
{
UIFont *font = [self fontWithSize:fontSize];
NSMutableDictionary *attributed = [NSMutableDictionary dictionaryWithDictionary:@{NSFontAttributeName:font}];
if (color) {
[attributed setObject:color forKey:NSForegroundColorAttributeName];
}
return [[NSAttributedString alloc] initWithString:iconName attributes:attributed];
}
as image
我们采用Category的方式对UIImageView增加设置图片的方法,同时也可以对UIButton、UILabel等其他控件增加Category。这里会去读取UIImageView控件的bounds作为image的size。
[self.imageView setImageWithIcon:zicon_edit];
也可以自定义image的size、fontSize、tintColor等属性
UIImage *image = [ZanIconFont imageWithIcon:zicon_edit imageSize:CGSizeMake(30, 30) fontSize:20 tintColor:[UIColor redColor]];
部分实现
+ (UIImage *)imageWithIcon:(ZanIconName)iconName imageSize:(CGSize)imageSize fontSize:(CGFloat)fontSize tintColor:(UIColor *)tintColor
{
if (!iconName) {
NSAssert(iconName, @"icon object should not be nil, check if the font file is added to the application bundle and you're using the correct font name.");
return nil;
}
UIGraphicsBeginImageContextWithOptions(imageSize, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
NSMutableAttributedString *fontString = [ZanIconFont attributedStringWithIcon:iconName fontSize:fontSize color:tintColor];
CGSize iconSize = [fontString size] ;
CGFloat xOffset = (imageSize.width - iconSize.width) / 2.0;
CGFloat yOffset = (imageSize.height - iconSize.height) / 2.0;
CGRect rect = CGRectMake(xOffset, yOffset, iconSize.width, iconSize.height);
[fontString drawInRect:rect];
UIImage *iconImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return iconImage;
}
也支持在Storyboard上设置icon name,通过Category对UIImageView增加IBInspectable类型的 iconName属性,匹配iconNameString对应的Unicode。
@implementation UIImageView (ZanIconFont)
- (void)setIconName:(NSString *)iconNameString
{
ZanIconName iconName = [ZanIconFont iconNameWithString:iconNameString];
if (!iconName) {
return;
}
[self setImage:[ZanIconFont imageWithIcon:iconName imageSize:self.bounds.size tintColor:self.tintColor]];
}
@end
imageViewCategory
在Android中使用
可参考开源库 iconify
总结
起初Iconfont在web中使用比较流行,在App中使用较少,但是目前看来很多大厂的App也纷纷使用起来,Iconfont的接入给我们项目带来了很多的方便,同时也可以解决我们在模块化过程中不同模块之间重复图片的问题,总之利大于弊,小伙伴们赶紧用起来吧。
参考
使用IconFont减小iOS应用体积 dzenbot/Iconic