如何在iOS中动态加载字体(真实情况下)

18

我看到这个问题被问答了很多次,但是我没有看到一个真正的答案。

常见的“解决方案”有:

  • 将字体添加到应用程序包中,并在info.plist文件中注册它。
  • 使用自定义的字体解析和渲染库(例如Zynga的FontLabel)。
  • 无法完成。

那么问题来了:如何在iOS下动态加载字体? 动态加载字体意味着在应用程序编译时不知道任何给定的字体。


你有研究过 CTFontManagerCreateFontDescriptorsFromURL 吗? - Bejmax
请参考https://dev59.com/tGox5IYBdhLWcg3wcT3r#12497630。我刚试了一下,成功加载并使用了字体。为了“在应用编译时不知道任何给定字体的情况下加载”,您可以下载字体并将其保存在文档目录中,然后按照代码示例进行操作。 - bobnoble
6个回答

25

字体可以轻松地从任何位置或任何字节流中动态加载。请参阅此处的文章:http://www.marco.org/2012/12/21/ios-dynamic-font-loading

NSData *inData = /* your font-file data */;
CFErrorRef error;
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)inData);
CGFontRef font = CGFontCreateWithDataProvider(provider);
if (! CTFontManagerRegisterGraphicsFont(font, &error)) {
    CFStringRef errorDescription = CFErrorCopyDescription(error)
    NSLog(@"Failed to load font: %@", errorDescription);
    CFRelease(errorDescription);
}
CFRelease(font);
CFRelease(provider);
  • 您不必将字体放入捆绑包中。
  • 您无需在info.plist中显式注册字体。

另请参见: https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_FontManager_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008278

https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGFont/Reference/reference.html#//apple_ref/c/func/CGFontCreateWithDataProvider


在加载字体时,是否可以指定要注册的字体名称? - user102008
@user102008 看起来似乎不可能。 - Ark-kun
@adib:我说的是设置名称,而不是获取名称。 - user102008
这个方法能否与Storyboard/XIB一起使用?还是说所有自定义字体都必须在应用程序启动时加载? - swalkner
在此问题的解决方案可以在 https://dev59.com/2GAf5IYBdhLWcg3wOQYL 找到。 - dev gr
显示剩余2条评论

10

最近Marco的一篇文章非常棒,标题为动态加载iOS字体

NSData *inData = /* your decrypted font-file data */;
CFErrorRef error;
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)inData);
CGFontRef font = CGFontCreateWithDataProvider(provider);
if (! CTFontManagerRegisterGraphicsFont(font, &error)) {
    CFStringRef errorDescription = CFErrorCopyDescription(error)
    NSLog(@"Failed to load font: %@", errorDescription);
    CFRelease(errorDescription);
}
CFRelease(font);
CFRelease(provider);

2
这是一个非常有趣的情况。我发现了Marco的帖子,然后去回答了一堆SO上的问题,使用了同样的代码。但是后来我想,创建一个自问自答的问题可能会更好,这样答案就更容易被发现。然后管理员删除了我的自己回答自己问题的答案,其中包含相同的代码 =( - Ark-kun
加载字体时是否可以指定要注册的字体名称? - user102008
你需要使用 #import <CoreText/CoreText.h> - MrTristan

2

这里是@mt81关于Swift 3的更新答案:

guard
    let path = "Path to some font file",
    let fontFile = NSData(contentsOfFile: path)
else {
    print "Font file not found?"
}

guard let provider = CGDataProvider(data: fontFile)
else {
    print "Failed to create DataProvider"
}

let font = CGFont(provider)
let error: UnsafeMutablePointer<Unmanaged<CFError>?>? = nil

guard CTFontManagerRegisterGraphicsFont(font, error) else {
    guard
        let unError = error?.pointee?.takeUnretainedValue(),
        let description = CFErrorCopyDescription(unError)
    else {
        print "Unknown error"
    }
    print description
}

2
// Note : add "CoreText.framework" into your project to support following code

// Put loadCustomFont function inside app delegate or any shared class to access any where in code...

    -(void)loadCustomFont:(NSMutableArray *)customFontFilePaths{

        for(NSString *fontFilePath in customFontFilePaths){

            if([[NSFileManager defaultManager] fileExistsAtPath:fontFilePath]){

                NSData *inData = [NSData dataWithContentsOfFile:fontFilePath];
                CFErrorRef error;
                CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
                CGFontRef font = CGFontCreateWithDataProvider(provider);
                // NSString *fontName = (__bridge NSString *)CGFontCopyFullName(font);
                if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
                    CFStringRef errorDescription = CFErrorCopyDescription(error);
                    NSLog(@"Failed to load font: %@", errorDescription);
                    CFRelease(errorDescription);
                }
                CFRelease(font);
                CFRelease(provider);
            }
        }
    }

    // Use as follow inside your view controller...

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        // pass all font files name into array which you want to load dynamically...
        NSMutableArray *customFontsPath = [[NSMutableArray alloc] init];
        NSArray *fontFileNameArray = [NSArray arrayWithObjects:@"elbow_v001.ttf",@"GothamRnd-MedItal.otf", nil];

        for(NSString *fontFileName in fontFileNameArray){

            NSString *fileName = [fontFileName stringByDeletingPathExtension];
            NSString *fileExtension = [fontFileName pathExtension];
            [customFontsPath addObject:[[NSBundle mainBundle] pathForResource:fileName ofType:fileExtension]];
        }


        AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        // load custom font into memory...
        [appDel loadCustomFont:customFontsPath];

        // Use font as below
        [lblName setFont:[UIFont fontWithName:@"Elbow v100" size:15.0]];
        [lblName2 setFont:[UIFont fontWithName:@"Gotham Rounded" size:20.0]];
    }

2
如果你要这样做,为什么不使用CTFontManagerRegisterFontsForURL()呢? - user102008
@user102008 因为你无法从该URL中知道你注册的字体是什么。通过使用CGFontRef可以获取名称,然后再使用普通的UIFont方法。 - adib
1
如果应用程序挂起,请使用此链接:https://dev59.com/2GAf5IYBdhLWcg3wOQYL - Pau Ballada

2

从服务器下载TTF文件?

如果您要下载TTF文件,则可以按照以下步骤将自定义字体注册到iOS字体管理器中,此代码片段还可以处理TTF文件更新(字体更新):

+(void)registerFontsAtPath:(NSString *)ttfFilePath
{
    NSFileManager * fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:ttfFilePath] == YES)
    {
        [UIFont familyNames];//This is here for a bug where font registration API hangs for forever.

        //In case of TTF file update : Fonts are already registered, first de-register them from Font Manager
        CFErrorRef cfDe_RegisterError;
       bool fontsDeregistered = CTFontManagerUnregisterFontsForURL((__bridge CFURLRef)[NSURL fileURLWithPath:ttfFilePath], kCTFontManagerScopeNone, &cfDe_RegisterError);


        //finally register the fonts with Font Manager,
        CFErrorRef cfRegisterError;
        bool fontsRegistered= CTFontManagerRegisterFontsForURL((__bridge CFURLRef)[NSURL fileURLWithPath:ttfFilePath], kCTFontManagerScopeNone, &cfRegisterError);
}

1
这里是Swift版本:

let inData: NSData = /* your font-file data */;
let error: UnsafeMutablePointer<Unmanaged<CFError>?> = nil
let provider = CGDataProviderCreateWithCFData(inData)
if let font = CGFontCreateWithDataProvider(provider) {
    if (!CTFontManagerRegisterGraphicsFont(font, error)) {
        if let unmanagedError = error.memory {
            let errorDescription = CFErrorCopyDescription(unmanagedError.takeUnretainedValue())
            NSLog("Failed to load font: \(errorDescription)");
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接