如何将自定义字体添加到Cocoapod?

31

我想在Cocoapod中使用自定义字体,但是我找不到有关在静态库中使用自定义字体的任何信息。由于没有info.plist文件,因此无法告诉应用程序使用哪种字体。

有什么好的建议吗?

6个回答

17

有一种方法可以在不向plist文件添加任何内容的情况下使用自定义字体。

    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSURL *fontURL = [bundle URLForResource:<#fontName#> withExtension:@"otf"/*or TTF*/];
    NSData *inData = [NSData dataWithContentsOfURL:fontURL];
    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);
    }
    CFSafeRelease(font);
    CFSafeRelease(provider);

你需要使用 CFSafeRelease 函数才能使其正常工作。
void CFSafeRelease(CFTypeRef cf) {
    if (cf != NULL) {
        CFRelease(cf);
    }
}

来源:动态加载iOS字体

Swift等效代码:

extension UIFont {
    static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) -> Bool {
        guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {
            fatalError("Couldn't find font \(fontName)")
        }

        guard let fontDataProvider = CGDataProvider(url: fontURL as CFURL) else {
            fatalError("Couldn't load data from the font \(fontName)")
        }

        guard let font = CGFont(fontDataProvider) else {
            fatalError("Couldn't create font from data")
        }

        var error: Unmanaged<CFError>?
        let success = CTFontManagerRegisterGraphicsFont(font, &error)
        guard success else {
            print("Error registering font: maybe it was already registered.")
            return false
        }

        return true
    }
}

1
但是你不能在Storyboard中使用这些字体,对吧?除非你在启动时加载所有自定义字体... - swalkner
1
由于这是作为Cocoapod发送的,而且想法是使字体立即可用于消费该Pod的人员,那么registerFont将在何时何地被调用? - h.and.h
在你的库/框架的任何代码中加入 @RIP.Ben.Franklin - Adam

13

如果您在2018年或之后找到了这篇文章,我有两个步骤让定制字体能够在界面构建器支持下(XCode 9)工作:

  1. 将您的字体添加到框架的资源包中(在.podspec文件中)

    s.resources = "PodName/**/*.{ttf}"
    
  2. 使用Adam上面的答案在运行时加载字体

  3. #import <CoreText/CoreText.h>
    
    void CFSafeRelease(CFTypeRef cf) { // redefine this
      if (cf != NULL) {
        CFRelease(cf);
      }
    }
    
    
    + (void) loadFonts {
      NSBundle *frameworkBundle = [NSBundle bundleForClass:self.classForCoder];
      NSURL *bundleURL = [[frameworkBundle resourceURL] URLByAppendingPathComponent:@"PodName.bundle"];
      NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
    
      NSURL *fontURL = [bundle URLForResource:@"HindMadurai-SemiBold" withExtension:@"ttf"];
      NSData *inData = [NSData dataWithContentsOfURL:fontURL];
      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);
      }
      CFSafeRelease(font);
      CFSafeRelease(provider);
    }
    
  4. 执行 pod install 命令


嗨!感谢您的回答。到目前为止,它对我来说运行得非常好。有一个问题...您最终是什么时候调用了loadFonts方法? - jmg
我正在构建一个SDK,所以最终将其放入初始化方法中,在那里设置API密钥等,我可以相对确定它只会被调用一次,并且在我的库中的任何内容呈现之前得到保证。 - tommybananas
好的,仿真器上似乎可以运行,但是在设备上运行时字体不加载。你也遇到这个问题了吗?还是它“只是工作”了? - jmg
路径问题已解决 :) - jmg
在编辑您的podspec后,不要忘记运行“pod install”。 - Eden

12

如果我理解正确的话,您正在尝试通过Cocoapod提供字体,并且希望包括该Pod的iOS应用程序能够使用您的自定义字体。

这个post_install挂钩似乎有效:

Pod::Spec.new do |s|
  # ...
  s.resources = "Resources/*.otf"
  # ...
  s.post_install do |library_representation|
    require 'rexml/document'

    library = library_representation.library
    proj_path = library.user_project_path
    proj = Xcodeproj::Project.new(proj_path)
    target = proj.targets.first # good guess for simple projects

    info_plists = target.build_configurations.inject([]) do |memo, item|
      memo << item.build_settings['INFOPLIST_FILE']
    end.uniq
    info_plists = info_plists.map { |plist| File.join(File.dirname(proj_path), plist) }

    resources = library.file_accessors.collect(&:resources).flatten
    fonts = resources.find_all { |file| File.extname(file) == '.otf' || File.extname(file) == '.ttf' }
    fonts = fonts.map { |f| File.basename(f) }

    info_plists.each do |plist|
      doc = REXML::Document.new(File.open(plist))
      main_dict = doc.elements["plist"].elements["dict"]
      app_fonts = main_dict.get_elements("key[text()='UIAppFonts']").first
      if app_fonts.nil?
        elem = REXML::Element.new 'key'
        elem.text = 'UIAppFonts'
        main_dict.add_element(elem)
        font_array = REXML::Element.new 'array'
        main_dict.add_element(font_array)
      else
        font_array = app_fonts.next_element
      end

      fonts.each do |font|
        if font_array.get_elements("string[text()='#{font}']").empty?
          font_elem = REXML::Element.new 'string'
          font_elem.text = font
          font_array.add_element(font_elem)
        end
      end

      doc.write(File.open(plist, 'wb'))
    end
  end

这个钩子会查找用户项目,在第一个目标中(通过向CocoaPods请求真实目标,您可能可以完成此解决方案),它会查找其Info.plist文件(通常只有一个)。最后,如果没有找到钩子则创建UIAppFonts键的文件,并在字体名称未填充数组的情况下将其填充。


7
你的Pod技术真是厉害。 - Jasper Blues
我的 pod spec lint 验证规范的某些部分无效了。但是在安装后它能够正常工作并添加自定义字体,有什么建议吗? - Ikhsan Assaat
不,没有错误是不可能的。请开一个新问题详细说明你遇到的错误和你已经尝试过的方法,否则很难帮助你。 - yonosoytu
2
post_install的钩子已被弃用,因此无法使用它,这是正确的吗?https://github.com/CocoaPods/CocoaPods/issues/1637 - SreeHarsha
@SreeHarsha:是的,您不能再使用这个解决方案了,您需要另一种解决方法,就像您链接指向的问题一样。 - yonosoytu
1
@yonosoytu你能否在帖子中更新这些信息,这将对其他人有所帮助。 - SreeHarsha

6

Swift 5实现

我通过在我的Cocoapod中创建下面的类来解决这个问题,然后只需从我的主应用程序的AppDelegate.swift调用CustomFonts.loadAll()即可。之后,我可以在我的应用程序中像这样使用字体:

let myFont = CustomFonts.Style.regular.font

请注意,Style枚举不是必需的,只是一种方便的分离关注点的方式。您也可以直接调用:

let myFont = UIFont(name: "SourceSansPro-SemiBold", size: 14)

import CoreText

public class CustomFonts: NSObject {

  public enum Style: CaseIterable {
    case mono
    case regular
    case semibold
    public var value: String {
      switch self {
      case .mono: return "SourceCodePro-Medium"
      case .regular: return "SourceSansPro-Regular"
      case .semibold: return "SourceSansPro-SemiBold"
      }
    }
    public var font: UIFont {
      return UIFont(name: self.value, size: 14) ?? UIFont.init()
    }
  }

  // Lazy var instead of method so it's only ever called once per app session.
  public static var loadFonts: () -> Void = {
    let fontNames = Style.allCases.map { $0.value }
    for fontName in fontNames {
      loadFont(withName: fontName)
    }
    return {}
  }()

  private static func loadFont(withName fontName: String) {
    guard
      let bundleURL = Bundle(for: self).url(forResource: "[CococpodName]", withExtension: "bundle"),
      let bundle = Bundle(url: bundleURL),
      let fontURL = bundle.url(forResource: fontName, withExtension: "ttf"),
      let fontData = try? Data(contentsOf: fontURL) as CFData,
      let provider = CGDataProvider(data: fontData),
      let font = CGFont(provider) else {
        return
    }
    CTFontManagerRegisterGraphicsFont(font, nil)
  }

}

1

嗯,我不确定这是否是一个答案,但你也可以看一下CocoaPod,其中包含所需的字体,例如: https://github.com/parakeety/GoogleFontsiOS

该库包含许多字体,我需要Chivo,因此我添加了pod 'GoogleFontsiOS/Chivo',并使用它来替代自己编写的字体加载代码。


0

CTFontManagerRegisterFontsForURL 函数让生活更轻松。


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