1. 程式人生 > >在iOS開發中使用自定義字型

在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系統自帶的字型冊來檢視。具體請參考前面的步驟。