在iOS开发中使用自定义字体

在iOS的项目开发中经常遇到需要使用一些自定义的字体文件,比如仿宋_GB2312、方正小标宋_GBK等。之前我们为了使用这些自定义的字体,在应用的资源包中放入这些字体文件。因为字体文件通常比较大,有的一个字库就达到10M以上(拿方正小标宋_GBK这个字库来说就有13M之多),这样打包后的ipa文件的体积就可能会变得很大,对于只有个别的模块需要特殊的字体样式的应用来说很不划算,那么在iOS6.0以后苹果就开放了动态加载字体的权限。下面就iOS中使用字体的这两种方式进行介绍。

###使用静态字体

1、将字体文件拷贝到项目工程中,在Info.plist文件中添加Fonts provided by application的配置项,其中每一个Item对应的是字体文件的名称,如DS-DIGI.TTF。

2、使用时直接按照如下方式即可:

_textLabel1.font = [UIFont fontWithName:@"DS-Digital" size:40];

效果如下:

3、其他说明:

+ (UIFont *)fontWithName:(NSString *)fontName size:(CGFloat)fontSize;这个方法中需要指定的fontName不是前面设置的字体文件的文件名,而应该是字体的名称,如何获取字体的名称可以通过如下方式:

(1)打印出当前所有可用的字体,查找对应的字体名称

- (void)printAllFonts

{

    NSArray *fontFamilies = [UIFont familyNames];

    for (NSString *fontFamily in fontFamilies)

    {

        NSArray *fontNames = [UIFont fontNamesForFamilyName:fontFamily];

        NSLog (@"%@: %@", fontFamily, fontNames);

    }

}

(2)通过Mac自带的字体册查看字体的名称

直接双击字体即可打开字体册,如果系统没有安装该字体按照要求安装即可,然后可以在字体的详细信息中找到对应的字体的名称:

###使用动态字体

####1、动态下载自定义的字体

在网易新闻iOS客户端中可以使用自定义的字体,对于未下载的字体可先下载然后安装下次就能自动设置为该字体,效果如下:

下面就该功能简单介绍实现的步骤

#####(1)下载指定的字体文件到本地

第一次进入该页面会自动到服务器上获取可使用的字体的列表,示例如下:

[

{

"fontTitle": "华康圆体",

"regularName": "DFYuanW3-GB",

"boldName": "DFYuanW5-GB",

"author": "华康科技",

"date": "2012-10-11",

"fileUrl": "http://xxxx/font/dfgb_y.zip",

"fileSize": "3.3MB",

"previewUrl": "http://yyyy/font/ios_yuanti.png"

}

]

上面的内容指明了字体的名称,下载地址等信息,从上面的内容可以看出下载回来的字体文件是一个zip压缩包,再使用前还需要进行解压处理。

######1)下载字体文件

- (NSString *)downloadZipFile:(NSString *)fileUrl toPath:(NSString *)path

{

    NSError *error = nil;

    NSURL *url = [NSURL URLWithString:fileUrl];

    NSString *fileName = [url lastPathComponent];

    NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];

    if(!error)

    {

        NSString *zipPath = [path stringByAppendingPathComponent:fileName];

        [data writeToFile:zipPath options:0 error:&error];

        if(!error)

        {

            return zipPath;

        }

    }

    return nil;

}

######2)解压zip压缩包

iOS中解压zip压缩文件非常方便,使用ZipArchive这个开源项目按照如下的方式即可快速解压zip文件。

- (NSString *)expandZipFile:(NSString *)src toPath:(NSString *)desc

{

    ZipArchive *za = [[ZipArchive alloc] init];

    if ([za UnzipOpenFile:src])

    {

        BOOL ret = [za UnzipFileTo:desc overWrite:YES];//解压文件

        if(ret)

        {

            NSString *zipName = [src lastPathComponent];//获取zip文件的文件名

            [[NSFileManager defaultManager] removeItemAtPath:zipPath error:nil];//删除zip压缩包

            zipName = [zipName substringToIndex:[zipName rangeOfString:@".zip"].location];//获取解压到的文件夹

            return [self.downloadPath stringByAppendingPathComponent:zipName];

        }

    }

    return nil;

}

ZipArchive项目地址:https://github.com/mattconnolly/ZipArchive

#####(2)注册指定路径下的字体文件

下载回来的字体文件如果不做处理是不能直接使用的,使用前需要先注册然后才能使用,注册方式如下:

- (void)registerFont:(NSString *)fontPath

{

    NSData *dynamicFontData = [NSData dataWithContentsOfFile:fontPath];

    if (!dynamicFontData)

    {

        return;

    }

    CFErrorRef error;

    CGDataProviderRef providerRef = CGDataProviderCreateWithCFData((__bridge CFDataRef)dynamicFontData);

    CGFontRef font = CGFontCreateWithDataProvider(providerRef);

    if (! CTFontManagerRegisterGraphicsFont(font, &error))

    {

        //注册失败

        CFStringRef errorDescription = CFErrorCopyDescription(error);

        NSLog(@"Failed to load font: %@", errorDescription);

        CFRelease(errorDescription);

    }

    CFRelease(font);

    CFRelease(providerRef);

}

需要先引入#import <CoreText/CoreText.h>,CoreText框架。

#####(3)判断字体是否加载

在使用字体文件前最好是先判断字体是否已经被加载过了,判断方式如下:

- (BOOL)isFontDownloaded:(NSString *)fontName

{

    UIFont* aFont = [UIFont fontWithName:fontName size:12.0];

    BOOL isDownloaded = (aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame));

    return isDownloaded;

}

#####(4)其他说明

经测试注册过的字体在应用关闭后下次开启应用,判断字体是否加载时返回为NO,为了保证正常使用需要每次启动应用的时候先遍历一遍字体文件夹将里面的字体文件都再次注册一遍即可。参考代码如下:

//注册fonts目录下面的所有字体文件

NSArray *ary = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.downloadPath error:nil];

for (NSString *p1 in ary)

{

NSString *t1 = [self.downloadPath stringByAppendingPathComponent:p1];

NSArray *ary1 = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:t1 error:nil];

for (NSString *p1 in ary1)

{

NSString *t2 = [t1 stringByAppendingPathComponent:p1];

if([t2 rangeOfString:@".ttf"].location != NSNotFound)

{

[self registerFont:t2];

}

}

}

####2、动态下载苹果提供的字体

大多数的中文字体是有版权的,在应用中加入特殊中文字体还需要处理相应的版权问题。从iOS6开始,苹果就支持动态下载中文字体到系统中。

(1)苹果支持下载的字体列表

1)iOS6字体列表:http://support.apple.com/zh-cn/HT202599

2)iOS7字体列表:http://support.apple.com/zh-cn/HT5878

(2)官方提供的示例代码

访问https://developer.apple.com/library/ios/samplecode/DownloadFont/Introduction/Intro.html下载示例程序。针对示例程序简单介绍如下:

1)判断字体是否已经被下载过

UIFont* aFont = [UIFont fontWithName:fontName size:12.];

if (aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame)) {

// 字体已经被加载过,可以直接使用

return;

}

2)下载字体

根据字体的PostScript名称构建下载字体所需的参数:

//使用字体的PostScript名称构建一个字典

NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:fontName, kCTFontNameAttribute, nil];

//根据上面的字典创建一个字体描述对象

CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attrs);

//将字体描述对象放到一个数组中

NSMutableArray *descs = [NSMutableArray arrayWithCapacity:0];

[descs addObject:(__bridge id)desc];

CFRelease(desc);

下载字体文件:

_block BOOL errorDuringDownload = NO;

CTFontDescriptorMatchFontDescriptorsWithProgressHandler( (__bridge CFArrayRef)descs, NULL,  ^(CTFontDescriptorMatchingState state, CFDictionaryRef progressParameter) {

    //下载的进度   

double progressValue = [[(__bridge NSDictionary *)progressParameter objectForKey:(id)kCTFontDescriptorMatchingPercentage] doubleValue];

if (state == kCTFontDescriptorMatchingDidBegin) {

dispatch_async( dispatch_get_main_queue(), ^ {

//开始匹配

NSLog(@"Begin Matching");

});

} else if (state == kCTFontDescriptorMatchingDidFinish) {

dispatch_async( dispatch_get_main_queue(), ^ {

if (!errorDuringDownload) {

//字体下载完成

NSLog(@"%@ downloaded", fontName);

              //TODO:在此修改UI控件的字体样式

}

});

} else if (state == kCTFontDescriptorMatchingWillBeginDownloading) {

//开始下载

        NSLog(@"Begin Downloading");

        dispatch_async( dispatch_get_main_queue(), ^ {

        //TODO:在此显示下载进度提示

});

} else if (state == kCTFontDescriptorMatchingDidFinishDownloading) {

//下载完成

NSLog(@"Finish downloading");

dispatch_async( dispatch_get_main_queue(), ^ {

//TODO:在此修改UI控件的字体样式,隐藏下载进度提示

});

} else if (state == kCTFontDescriptorMatchingDownloading) {

//正在下载

NSLog(@"Downloading %.0f%% complete", progressValue);

dispatch_async( dispatch_get_main_queue(), ^ {

//TODO:在此修改下载进度条的数值

});

} else if (state == kCTFontDescriptorMatchingDidFailWithError) {

//下载遇到错误,获取错误信息

NSError *error = [(__bridge NSDictionary *)progressParameter objectForKey:(id)kCTFontDescriptorMatchingError];

NSLog(@"%@", [error localizedDescription]);

//设置下载错误标志

errorDuringDownload = YES;

}

    return (bool)YES;

});

(3)说明

1)使用动态下载中文字体的API可以动态地向iOS系统中添加字体文件,这些字体文件都是下载到系统的目录中(目录是/private/var/mobile/Library/Assets/com_apple_MobileAsset_Font/),所以并不会造成应用体积的增加,而且可以在多个应用中共享。

2)如何获取字体的PostScript和FontName?可以通过Mac系统自带的字体册来查看。具体请参考前面的步骤。

###参考资料

1、《动态下载苹果提供的多种中文字体》

2、《字体加载三种方式》

3、《在iOS程序中使用自定义字体》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容