How do I add a custom font to a Cocoapod?

前端 未结 5 732
一生所求
一生所求 2021-01-31 03:43

I want to use a custom font within a Cocoapod, but I can\'t find anything on using a custom font within a static library. As there is no info.plist file, there is no where to te

相关标签:
5条回答
  • 2021-01-31 04:24

    If I understand correctly, you are trying to provide a font with your Cocoapod, and you intent the iOS apps which include the pod to be able to use your custom font.

    This post_install hook seems to work:

    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
    

    The hook finds the user project, and in the first target (you probably can complete this solution by asking CocoaPods to give you the real target) it looks for its Info.plist file(s) (normally there is only one). Finally it looks for the UIAppFonts key of the file, creates it if not found, and fill the array with the font names if they are not already there.

    0 讨论(0)
  • 2021-01-31 04:35

    Well, idk if it can be an answer, but you can also look to cocoapod that has needed font, like this: https://github.com/parakeety/GoogleFontsiOS

    Library contains many fonts, i needed Chivo, so i added pod 'GoogleFontsiOS/Chivo' and used it instead of writing font-loading code by myself.

    0 讨论(0)
  • 2021-01-31 04:41

    There is a way of using a custom font without adding anything to the plist file.

        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);
    

    You also need the CFSafeRelease function for this to work.

    void CFSafeRelease(CFTypeRef cf) {
        if (cf != NULL) {
            CFRelease(cf);
        }
    }
    

    Source: Loading iOS fonts dynamically.

    Swift equivalent:

    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
        }
    }
    
    0 讨论(0)
  • 2021-01-31 04:43

    For those of you finding this in 2018+, I got custom fonts to work with interface builder support (XCode 9) with these two steps:

    1. Add your fonts to the resource bundle of your framework

      s.resources = "PodName/**/*.{ttf}"
      
    2. Load fonts at runtime using Adam's answer above

      #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);
      }
      
    0 讨论(0)
  • 2021-01-31 04:45

    Swift 5 implementation

    I was able to solve this by creating the below class in my Cocoapod, then just calling CustomFonts.loadAll() from my main app's AppDelegate.swift. After that I can just use a font like this in my app:

    let myFont = CustomFonts.Style.regular.font

    Note that the Style enum is not necessary, just a convenient way to separate concerns. You could also just call:

    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)
      }
    
    }
    
    0 讨论(0)
提交回复
热议问题