在iOS的項(xiàng)目開發(fā)中經(jīng)常遇到需要使用一些自定義的字體文件,比如仿宋_GB2312、方正小標(biāo)宋_GBK等。之前我們?yōu)榱耸褂眠@些自定義的字體,在應(yīng)用的資源包中放入這些字體文件。因?yàn)樽煮w文件通常比較大,有的一個(gè)字庫(kù)就達(dá)到10M以上(拿方正小標(biāo)宋_GBK這個(gè)字庫(kù)來說就有13M之多),這樣打包后的ipa文件的體積就可能會(huì)變得很大,對(duì)于只有個(gè)別的模塊需要特殊的字體樣式的應(yīng)用來說很不劃算,那么在iOS6.0以后蘋果就開放了動(dòng)態(tài)加載字體的權(quán)限。下面就iOS中使用字體的這兩種方式進(jìn)行介紹。
###使用靜態(tài)字體
1、將字體文件拷貝到項(xiàng)目工程中,在Info.plist文件中添加Fonts provided by application的配置項(xiàng),其中每一個(gè)Item對(duì)應(yīng)的是字體文件的名稱,如DS-DIGI.TTF。
2、使用時(shí)直接按照如下方式即可:
_textLabel1.font = [UIFont fontWithName:@"DS-Digital" size:40];
效果如下:
3、其他說明:
+ (UIFont *)fontWithName:(NSString *)fontName size:(CGFloat)fontSize;這個(gè)方法中需要指定的fontName不是前面設(shè)置的字體文件的文件名,而應(yīng)該是字體的名稱,如何獲取字體的名稱可以通過如下方式:
(1)打印出當(dāng)前所有可用的字體,查找對(duì)應(yīng)的字體名稱
- (void)printAllFonts
{
? ? NSArray *fontFamilies = [UIFont familyNames];
? ? for (NSString *fontFamily in fontFamilies)
? ? {
? ? ? ? NSArray *fontNames = [UIFont fontNamesForFamilyName:fontFamily];
? ? ? ? NSLog (@"%@: %@", fontFamily, fontNames);
? ? }
}
(2)通過Mac自帶的字體冊(cè)查看字體的名稱
直接雙擊字體即可打開字體冊(cè),如果系統(tǒng)沒有安裝該字體按照要求安裝即可,然后可以在字體的詳細(xì)信息中找到對(duì)應(yīng)的字體的名稱:
###使用動(dòng)態(tài)字體
####1、動(dòng)態(tài)下載自定義的字體
在網(wǎng)易新聞iOS客戶端中可以使用自定義的字體,對(duì)于未下載的字體可先下載然后安裝下次就能自動(dòng)設(shè)置為該字體,效果如下:
下面就該功能簡(jiǎn)單介紹實(shí)現(xiàn)的步驟
#####(1)下載指定的字體文件到本地
第一次進(jìn)入該頁面會(huì)自動(dòng)到服務(wù)器上獲取可使用的字體的列表,示例如下:
[
{
"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"
}
]
上面的內(nèi)容指明了字體的名稱,下載地址等信息,從上面的內(nèi)容可以看出下載回來的字體文件是一個(gè)zip壓縮包,再使用前還需要進(jìn)行解壓處理。
######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這個(gè)開源項(xiàng)目按照如下的方式即可快速解壓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項(xiàng)目地址:https://github.com/mattconnolly/ZipArchive
#####(2)注冊(cè)指定路徑下的字體文件
下載回來的字體文件如果不做處理是不能直接使用的,使用前需要先注冊(cè)然后才能使用,注冊(cè)方式如下:
- (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))
? ? {
? ? ? ? //注冊(cè)失敗
? ? ? ? CFStringRef errorDescription = CFErrorCopyDescription(error);
? ? ? ? NSLog(@"Failed to load font: %@", errorDescription);
? ? ? ? CFRelease(errorDescription);
? ? }
? ? CFRelease(font);
? ? CFRelease(providerRef);
}
需要先引入#import <CoreText/CoreText.h>,CoreText框架。
#####(3)判斷字體是否加載
在使用字體文件前最好是先判斷字體是否已經(jīng)被加載過了,判斷方式如下:
- (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)其他說明
經(jīng)測(cè)試注冊(cè)過的字體在應(yīng)用關(guān)閉后下次開啟應(yīng)用,判斷字體是否加載時(shí)返回為NO,為了保證正常使用需要每次啟動(dòng)應(yīng)用的時(shí)候先遍歷一遍字體文件夾將里面的字體文件都再次注冊(cè)一遍即可。參考代碼如下:
//注冊(cè)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、動(dòng)態(tài)下載蘋果提供的字體
大多數(shù)的中文字體是有版權(quán)的,在應(yīng)用中加入特殊中文字體還需要處理相應(yīng)的版權(quán)問題。從iOS6開始,蘋果就支持動(dòng)態(tài)下載中文字體到系統(tǒng)中。
(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下載示例程序。針對(duì)示例程序簡(jiǎn)單介紹如下:
1)判斷字體是否已經(jīng)被下載過
UIFont* aFont = [UIFont fontWithName:fontName size:12.];
if (aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame)) {
// 字體已經(jīng)被加載過,可以直接使用
return;
}
2)下載字體
根據(jù)字體的PostScript名稱構(gòu)建下載字體所需的參數(shù):
//使用字體的PostScript名稱構(gòu)建一個(gè)字典
NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:fontName, kCTFontNameAttribute, nil];
//根據(jù)上面的字典創(chuàng)建一個(gè)字體描述對(duì)象
CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attrs);
//將字體描述對(duì)象放到一個(gè)數(shù)組中
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) {
? ? //下載的進(jìn)度? ?
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:在此顯示下載進(jìn)度提示
});
} else if (state == kCTFontDescriptorMatchingDidFinishDownloading) {
//下載完成
NSLog(@"Finish downloading");
dispatch_async( dispatch_get_main_queue(), ^ {
//TODO:在此修改UI控件的字體樣式,隱藏下載進(jìn)度提示
});
} else if (state == kCTFontDescriptorMatchingDownloading) {
//正在下載
NSLog(@"Downloading %.0f%% complete", progressValue);
dispatch_async( dispatch_get_main_queue(), ^ {
//TODO:在此修改下載進(jìn)度條的數(shù)值
});
} else if (state == kCTFontDescriptorMatchingDidFailWithError) {
//下載遇到錯(cuò)誤,獲取錯(cuò)誤信息
NSError *error = [(__bridge NSDictionary *)progressParameter objectForKey:(id)kCTFontDescriptorMatchingError];
NSLog(@"%@", [error localizedDescription]);
//設(shè)置下載錯(cuò)誤標(biāo)志
errorDuringDownload = YES;
}
? ? return (bool)YES;
});
(3)說明
1)使用動(dòng)態(tài)下載中文字體的API可以動(dòng)態(tài)地向iOS系統(tǒng)中添加字體文件,這些字體文件都是下載到系統(tǒng)的目錄中(目錄是/private/var/mobile/Library/Assets/com_apple_MobileAsset_Font/),所以并不會(huì)造成應(yīng)用體積的增加,而且可以在多個(gè)應(yīng)用中共享。
2)如何獲取字體的PostScript和FontName?可以通過Mac系統(tǒng)自帶的字體冊(cè)來查看。具體請(qǐng)參考前面的步驟。
###參考資料