我能在iPhone应用中嵌入自定义字体吗?

791

我想制作一个应用程序以包含自定义字体以渲染文本,加载它,然后与标准的UIKit元素(如UILabel)一起使用。这是否可行?


5
在斯坦福大学的CS193p(iPhone应用程序开发)课程中,埃文指出在设备上安装自己的字体是“很费事”的,但至少这意味着这是可能的 :-) - Ilya Birman
2
iPhone 3.2 允许自定义字体,但仅限于 iPad(请参见下面的答案)。 - samvermette
6
最后,我使用了图像。 - Airsource Ltd
2
哇...经过所有这些,答案仍然是图像...难以置信。我真的希望能够实现自定义字体与系统字体具有相同的功能。唉... - d2burke
有用的教程:http://codewithchris.com/common-mistakes-with-adding-custom-fonts-to-your-ios-app/ - Suragch
1
我已经大幅削减了原问题以反映自从我提问以来过去的十年。我鼓励其他人适当地减少他们的评论和答案。 - Airsource Ltd
32个回答

646
iOS 3.2及更高版本支持此功能。直接从文档中得知:

 

自定义字体支持
  希望使用自定义字体的应用程序现在可以将这些字体包含在其应用程序包中,并通过在其Info.plist文件中包含UIAppFonts键来向系统注册这些字体。此键的值是一个字符串数组,用于标识应用程序包中的字体文件。当系统看到该键时,它会加载指定的字体并使其可用于应用程序。

一旦在Info.plist中设置了字体,您可以像其他字体一样在IB或程序中使用自定义字体。

Apple Developer论坛上有一个正在进行的主题:
https://devforums.apple.com/thread/37824 (需要登录)

这里还有一个成果显著的简单三步教程(已删除损坏的链接)

  1. 使用Xcode将自定义字体文件添加到项目中作为资源
  2. Info.plist文件中添加一个名为UIAppFonts的键。
  3. 将此键设置为数组
  4. 对于每个字体,请将您的字体文件的完整名称(包括扩展名)作为UIAppFonts数组中的项目输入
  5. 保存Info.plist
  6. 现在,在您的应用程序中,您只需调用[UIFont fontWithName:@"CustomFontName" size:12]即可获取自定义字体,并将其与您的 UILabels UITextViews 等一起使用。

还要确保字体在您的复制束资源中。


14
以下是iOS4的逐步教程:http://blog.beefyapps.com/2010/06/custom-fonts-in-ios-4/。 - pm_labs
15
这篇教程非常好。http://shang-liang.com/blog/custom-fonts-in-ios4/ 重要的是,[UIFont fontWithName:...] 中的 NSString 参数是字体的 操作系统 名称而不是文件名。 - Willster
153
如果有人想花三个小时安装FontForge来查找iOS所需的实际PostScript名称,那么我要指出,您可以在字体册中选中该字体并按Cmd+I快捷键来查找这些信息,无需如此麻烦。 - Daniel Wood
42
为了找到正确的字体名称,我努力寻找并列出已安装的字体。这非常有帮助。以下是代码:for (NSString *familyName in [UIFont familyNames]) { NSLog(@"Family %@", familyName); NSLog(@"Names = %@", [UIFont fontNamesForFamilyName:familyName]); } - Steve Potter
26
如果您的代码无法正常工作,请确保在“Build Phases”->“Copy Bundle Resouces”中找到字体文件。 - Mickey
显示剩余8条评论

300

编辑:自iOS 3.2起,此功能已内置。如果需要支持3.2之前的版本,仍然可以使用此解决方案。

我创建了一个简单的模块,扩展了UILabel并处理加载.ttf文件的操作。我在Apache许可证下发布了它,并将其放在了Github上这里

重要的文件是FontLabel.hFontLabel.m

它使用了来自Genericrich's答案的一些代码。

这里浏览源代码。

或者

  • 将字体文件复制到资源中

  • 为您的Info.plist文件添加一个键,叫做UIAppFonts。 (“应用程序提供的字体”)

  • 将此键设置为数组

  • 对于每种字体,将字体文件的完整名称(包括扩展名)输入UIAppFonts数组项

  • 保存Info.plist

  • 现在,在您的应用程序中,只需调用[UIFont fontWithName:@"CustomFontName" size:15]即可获得自定义字体,以与您的UILabelsUITextViews等一起使用。

了解更多信息


2
我尝试使用了你的代码,但它经常会因字体不同而崩溃。例如,请尝试使用此处的非洲或提基字体http://www.fontspace.com/category/tiki。 - 4thSpace
2
这个库非常好用。 不过我需要在垂直间距方面寻求帮助。我无法弄清如何做到。 所以我有这个消息.numberOfLines = 3; 我该如何控制第1行、第2行和第3行之间的垂直间距?谢谢, Tee - teepusink
如果按照这种方法,我们必须实现绘图逻辑。如果我们想直接在UILabel实例上显示字体,而不是自定义的UILabel子类呢? - Raj Pawan Gumdal
2
@commanda 你好 commanda,你在回答中提供的链接似乎无法访问。 - Parth Bhatt
5
你应该使用提供在 https://dev59.com/1HRC5IYBdhLWcg3wP-n5#2616101 的解决方案,而不是 FontLabel,因为 CocoaTouch 现在已经提供了这个功能。 - commanda
2
@ParthBhatt: 我不知道为什么这个代码库不再托管在Zynga上,但是它可以在https://github.com/kballard/fontlabel访问。 - Lily Ballard

117

在中使用自定义字体有一个简单的方法:

  1. 将字体文件(例如Chalkduster.ttf)添加到XCode项目的资源文件夹中。
  2. 打开info.plist,添加一个名为UIAppFonts的新键。此键的类型应为数组。
  3. 将自定义字体名称及其扩展名(Chalkduster.ttf)添加到此数组中。
  4. 现在您可以在应用程序中使用[UIFont fontWithName:@"Chalkduster" size:16]

不幸的是,IB不允许使用自定义字体初始化标签。请参见此问题以解决此问题。我最喜欢的解决方案是使用自定义UILabel子类:

@implementation CustomFontLabel

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super initWithCoder: decoder])
    {
        [self setFont: [UIFont fontWithName: @"Chalkduster" size: self.font.pointSize]];
    }
    return self;
}

@end

3
似乎支持至3.2版本,而非仅限于4.0以上版本。 - Jesse Rusak
1
谢谢!我遇到的问题是自定义字体似乎比内置字体略微向上偏移(下方有额外空间)。我尝试使用GitHub上的FontLabel仓库,有时有所帮助,但并不总是有效。 - Joe D'Andrea
12
值得注意的是,传递给UIFont构造函数的字符串不是文件名减去扩展名,而是字体的内部名称。我曾经使用了一个被我缩短了文件名的字体,但无法成功加载,直到我使用完整的字体名称来加载时才成功。 - slayton
5
“Slayton 刚刚说的非常重要。”“UIFont fontWithName”需要字体的“全名”,可以在Font Book中打开它并选择“预览-->显示字体信息”来查看。 - electromaggot

51

在 Info.plist 文件中添加条目“Fonts provided by application”,并将字体名称以字符串形式包含其中:

Fonts provided by application
           Item 0        myfontname.ttf
           Item 1        myfontname-bold.ttf
           ...

然后确认你的字体是否已经包含,可以运行以下命令:

for (NSString *familyName in [UIFont familyNames]) {
    for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
         NSLog(@"%@", fontName);
    }
}
请注意,您的ttf文件名可能与您在为标签设置字体时使用的名称不同(您可以使用上面的代码获取"fontWithName"参数):
[label setFont:[UIFont fontWithName:@"MyFontName-Regular" size:18]];

36

编辑:自iOS3.2起,此答案已过时,请使用UIAppFonts。

我成功加载自定义UIFont的唯一方法是通过私有的GraphicsServices框架。

下面的代码将加载应用程序主包中所有的.ttf字体:

BOOL GSFontAddFromFile(const char * path);
NSUInteger loadFonts()
{
    NSUInteger newFontCount = 0;
    for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:@"ttf" inDirectory:nil])
        newFontCount += GSFontAddFromFile([fontFile UTF8String]);
    return newFontCount;
}

一旦字体被加载,它们可以像苹果提供的字体一样使用:

NSLog(@"Available Font Families: %@", [UIFont familyNames]);
[label setFont:[UIFont fontWithName:@"Consolas" size:20.0f]];

如果API将来消失,甚至可以在运行时加载GraphicsServices:

#import <dlfcn.h>
NSUInteger loadFonts()
{
    NSUInteger newFontCount = 0;
    NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.GraphicsServices"];
    const char *frameworkPath = [[frameworkBundle executablePath] UTF8String];
    if (frameworkPath) {
        void *graphicsServices = dlopen(frameworkPath, RTLD_NOLOAD | RTLD_LAZY);
        if (graphicsServices) {
            BOOL (*GSFontAddFromFile)(const char *) = dlsym(graphicsServices, "GSFontAddFromFile");
            if (GSFontAddFromFile)
                for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:@"ttf" inDirectory:nil])
                    newFontCount += GSFontAddFromFile([fontFile UTF8String]);
        }
    }
    return newFontCount;
}

在OS 3.0中,您绝对需要使用第二个示例,不要忘记导入。对我来说效果很好。 - M. Ryan
什么是结论?有哪些替代方法? - Raj Pawan Gumdal
这是非常非常私密的。如果您能够自己绘制文本,可以使用CGFontCreateWithDataProviderUIAppFonts可用于3.2+。 - rpetrich
1
还应该注意到,对于我尝试过的任何自定义字体,[NSString sizeWithFont:[UIFont fontWithName:@"customFont" size:14]]; 都会返回0宽度和高度。 - Luke Mcneice
记录所有字体名称的日志+1。我已经正确加载了我的ttf文件,但在[UIFont fontWithName:@"fake name" size:22.0f]]中使用了错误的字体名称。 - Eric Brotto
显示剩余2条评论

34

使用 iOS 8+ 和 Xcode 6+ 您可以轻松完成此操作。以下是步骤:

1)将字体拖放到 Xcode Supporting Files 文件夹中。不要忘记在添加到目标部分中标记您的应用程序。从此时起,您可以在 IB 中使用此字体,并从字体调色板中选择它。

enter image description here

2)要使此字体在设备上可用,请打开您的 info.plist 并添加 Fonts provided by application 键。它将包含 Item 0 键,您必须将字体名称添加为值。字体名称可能与字体文件名称不同。但首先,请尝试在大多数情况下添加您的文件名,这通常有效。

enter image description here

如果不行,这篇文章 总是帮助了我。

这里是来自该文章的 Swift 代码片段,以帮助您找到字体名称。

func allFonts(){

   for family in UIFont.familyNames(){

       println(family)


       for name in UIFont.fontNamesForFamilyName(family.description)
       {
           println("  \(name)")
       }

   }

}

编辑

我想提醒一下,您需要将字体文件添加到您的目标构建阶段复制包资源中。如果没有这样做,您将无法在设备上看到您的字体,并可能导致意外行为。

例如,当UITextField有自定义字体,但此字体不在复制包资源中时,我遇到了一个bug。当我转场到带有此textfieldviewcontroller时,会有大约4秒的延迟,直到viewDidLoad函数被调用。解决字体问题后,此延迟被消除。因此,建议您仔细检查两次。(rdar://20028250)顺便说一句,我无法重现这个错误,但我确定问题出在字体上。


1
只是一点提醒。即使在IB中正确配置了自定义字体,启动屏幕中也无法使用它们。这可能是合乎逻辑的。 - Rivera
@Rivera 哇,好棒的笔记!我不知道那个。我认为可能的替代方案是使用带有文本的UIImage。 - Dima Deplov
@Rivera,谢谢你提供的信息 - 这个在哪里有记录吗?我一直在重复这些步骤,以为我错过了什么步骤。 - spongessuck
@spongessuck,不确定你的问题是什么,但有一些文档可供参考 https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html#//apple_ref/doc/uid/TP40009542-CH4-SW62 - Dima Deplov

27

我是这样做的:

加载字体:

- (void)loadFont{
  // Get the path to our custom font and create a data provider.
  NSString *fontPath = [[NSBundle mainBundle] pathForResource:@"mycustomfont" ofType:@"ttf"]; 
  CGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]);

  // Create the font with the data provider, then release the data provider.
  customFont = CGFontCreateWithDataProvider(fontDataProvider);
  CGDataProviderRelease(fontDataProvider); 
}

现在,在您的 drawRect:中,可以执行类似以下操作:

-(void)drawRect:(CGRect)rect{
    [super drawRect:rect];
    // Get the context.
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);
    // Set the customFont to be the font used to draw.
    CGContextSetFont(context, customFont);

    // Set how the context draws the font, what color, how big.
    CGContextSetTextDrawingMode(context, kCGTextFillStroke);
    CGContextSetFillColorWithColor(context, self.fontColor.CGColor);
    UIColor * strokeColor = [UIColor blackColor];
    CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
    CGContextSetFontSize(context, 48.0f);

    // Create an array of Glyph's the size of text that will be drawn.
    CGGlyph textToPrint[[self.theText length]];

    // Loop through the entire length of the text.
    for (int i = 0; i < [self.theText length]; ++i) {
        // Store each letter in a Glyph and subtract the MagicNumber to get appropriate value.
        textToPrint[i] = [[self.theText uppercaseString] characterAtIndex:i] + 3 - 32;
    }
    CGAffineTransform textTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    CGContextSetTextMatrix(context, textTransform);
    CGContextShowGlyphsAtPoint(context, 20, 50, textToPrint, [self.theText length]);
}

基本上,您需要通过文本进行一些暴力循环,并调整魔数来查找字体中的偏移量(这里,看到我使用了29),但它可以工作。

此外,您必须确保字体可以合法嵌入。 大多数字体都不行,并且有专门从事此类事情的律师,因此请注意。


1
我相信它能够很好地工作,但是我(真的真的)不想编写自己的布局引擎来处理UILabel和UITextView目前为我处理的一切 - 特别是单词换行、定位和编辑。 - Airsource Ltd
1
据我看,这仅适用于大写字符,因此需要调用 'uppercaseString' 方法。 - Martin Cote
2
我要点踩,因为“魔法数字”技巧是一种纯粹的黑客技巧,实际上并不起作用。真正的解决方案是从TTF文件中获取cmap表。 - Martin Cote
1
这是一个关于如何直接使用Core Graphics字体的有用信息。只是为了给未来的读者更新一下:在较新的操作系统中,可以并且应该使用Core Text。另外,也可以使用CGContextShowTextAtPoint()函数,尽管它不支持UTF-8编码。 - Ivan Vučica

21

是的,您可以包含自定义字体。请参考UIFont文档中的fontWithName:size:方法。

1) 确保将字体包含在资源文件夹中。

2) 字体的“名称”不一定是文件名。

3) 确保您有合法使用该字体的权利。通过将它包含在您的应用程序中,您也在分发该字体,因此您需要获得相应的使用权。


你自己做成功了吗?我无法让它工作,而且谷歌搜索只发现其他曾经尝试过但失败的人。 - Airsource Ltd
是的,我有。再次确保您使用正确的字体名称。 - August
1
当你说“字体的‘名称’不一定是文件名”时,你如何找到正确的名称? - Keith Fitzgerald
7
是的,这个答案是错误的。你不能直接做到这一点。 - Jonathan Sterling
iOS 9更新,UIFont文档中没有提到任何关于自定义字体的内容。 - Cbas

20

如果你正在使用 xcode 4.3 版本,则需要在 Copy Bundle Resources 中的 Build Phase 下添加字体,参考自 https://stackoverflow.com/users/1292829/arne 在线讨论中提供的解决方案:Custom Fonts Xcode 4.3。这对我有效,下面是我为实现应用程序自定义字体所采取的步骤:

  1. 将字体文件添加到项目中。我将 OTF(或 TTF)文件拖放到新创建的组中,并接受 xcode 的选择,即将文件复制到项目文件夹中。
  2. 创建 UIAppFonts 数组,并将您的字体名称作为数组项列出。只写字体的名称,不包括后缀名(例如"GothamBold","GothamBold-Italic")。
  3. 单击屏幕左侧的 Project Navigator 上方的 project name
  4. 单击出现在 xcode 主要区域的 Build Phases 选项卡。
  5. 展开"Copy Bundle Resources"部分,然后单击 "+" 添加字体。
  6. 从文件导航器中选择字体文件。
  7. 对于需要 添加到项目中 的每个字体,都要重复执行以上步骤。

14

我建议您按照我的一个最喜欢的简短教程进行操作:http://codewithchris.com/common-mistakes-with-adding-custom-fonts-to-your-ios-app/,此信息来源于该教程。

步骤1 - 将.ttf或.otf文件从Finder拖到您的项目中

注意 - 确保在主应用程序目标上单击“添加到目标”框

将字体文件拖入您的项目并单击以将其添加到目标

如果您忘记点击添加到目标,则在项目层次结构中单击字体文件,在右侧面板中单击目标成员资格部分的主应用程序目标

如果您忘记了如何将字体添加到应用程序目标

确保您的字体是应用程序目标的一部分,请确保它们显示在Build PhasesCopy Bundle Resources

如何检查资源以查看字体是否属于应用程序目标

步骤2 - 将字体文件名添加到您的Plist中

转到Info部分的Custom iOS Target Properties,并在该部分中的项目中添加一个名为Fonts provided by application的键(您键入时应该会看到它作为选项出现,并且它将设置为一个数组。单击小箭头以打开数组项,并输入您要添加的.ttf或.otf文件的名称,以让您的应用程序知道这些是您希望可用的字体文件。

注意 - 如果此步骤后您的应用程序崩溃,请检查您在此处添加的项目的拼写

已添加自定义字体的示例plist

步骤3 - 找出字体名称,以便您可以调用它们

经常情况下,您的应用程序所看到的字体名称与您基于该字体文件名认为的不同,请将此代码放入您的代码中,并查看您的应用程序生成的日志,以查看要在代码中调用哪个字体名称

Swift

for family: String in UIFont.familyNames(){
  print("\(family)")
  for names: String in UIFont.fontNamesForFamilyName(family){
      print("== \(names)")
  }
}

Objective C

for (NSString* family in [UIFont familyNames]){
    NSLog(@"%@", family);
    for (NSString* name in [UIFont fontNamesForFamilyName: family]){
        NSLog(@"  %@", name);
    }
}

你的日志应该长成这个样子:

搜索日志以查找字体名称示例

第四步 - 使用从第三步中得到的名称使用您的新自定义字体

Swift

 label.font = UIFont(name: "SourceSansPro-Regular", size: 18)

Objective C

 label.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:18];

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