2015-05-28 14 views
21

Ich habe benutzerdefinierte Schriftart zu einem Framework hinzugefügt. Ich habe alle Schritte befolgt, aber es funktioniert nicht.Xcode: Verwenden benutzerdefinierter Schriftarten in Dynamic Framework

Ich kann die Schriftart in Interface Builder festlegen, aber wenn ich das Projekt erstelle zeigt es diese Schriftart nicht auf dem Simulator/Gerät.

+0

Haben Sie die Methode in meiner Antwort getestet? Ich verwende es in einigen Apps, die benutzerdefinierte Schriftarten verwenden, die mit einem dynamischen Framework gebündelt sind, ohne dass die Schriftarten zum Hauptprojekt hinzugefügt werden müssen. –

Antwort

13

Ich bin hier ein bisschen spät, aber ich nahm PetahChristian-Lösung und eine Swift-Version in Form einer Erweiterung erstellt. Das funktioniert für mich. Ich habe festgestellt, dass wenn Sie versuchen, eine Schriftart mit einem Schriftartnamen und einer Größe zu erhalten, indem Sie die reguläre Methode verwenden, die immer im Hauptpaket für die Schriftartdatei angezeigt wird. Es gibt keine Methode, die eine Paketkennung als Parameter verwendet. Es wäre schön, wenn Apple einen machen würde.

Swift:

public extension UIFont { 

public static func registerFontWithFilenameString(filenameString: String, bundle: Bundle) { 

    guard let pathForResourceString = bundle.path(forResource: filenameString, ofType: nil) else { 
     print("UIFont+: Failed to register font - path for resource not found.") 
     return 
    } 

    guard let fontData = NSData(contentsOfFile: pathForResourceString) else { 
     print("UIFont+: Failed to register font - font data could not be loaded.") 
     return 
    } 

    guard let dataProvider = CGDataProvider(data: fontData) else { 
     print("UIFont+: Failed to register font - data provider could not be loaded.") 
     return 
    } 

    guard let fontRef = CGFont(dataProvider) else { 
     print("UIFont+: Failed to register font - font could not be loaded.") 
     return 
    } 

    var errorRef: Unmanaged<CFError>? = nil 
    if (CTFontManagerRegisterGraphicsFont(fontRef, &errorRef) == false) { 
     print("UIFont+: Failed to register font - register graphics font failed - this font may have already been registered in the main bundle.") 
    } 
} 
+0

@Ryan gute bearbeiten –

6

Sie können gebündelte benutzerdefinierte Schriftarten aus Ihrem dynamischen Framework laden und verwenden, indem Sie die Methode + load in Ihrem Framework implementieren.

In der load Methode suchen Sie die Schriftarten im Bundle und registrieren sie dann. Dadurch stehen sie der App zur Verfügung, ohne sie im Hauptprojekt angeben zu müssen.

+ (void)load 
{ 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     // Dynamically load bundled custom fonts 

     [self bible_loadFontWithName:kBIBLECustomFontBoldName]; 
     [self bible_loadFontWithName:kBIBLECustomFontBoldItalicName]; 
     [self bible_loadFontWithName:kBIBLECustomFontItalicName]; 
     [self bible_loadFontWithName:kBIBLECustomFontRegularName]; 
    }); 
} 

+ (void)bible_loadFontWithName:(NSString *)fontName 
{ 
    NSString *fontPath = [[NSBundle bundleForClass:[BIBLE class]] pathForResource:fontName ofType:@"otf"]; 
    NSData *fontData = [NSData dataWithContentsOfFile:fontPath]; 

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)fontData); 

    if (provider) 
    { 
     CGFontRef font = CGFontCreateWithDataProvider(provider); 

     if (font) 
     { 
      CFErrorRef error = NULL; 
      if (CTFontManagerRegisterGraphicsFont(font, &error) == NO) 
      { 
       CFStringRef errorDescription = CFErrorCopyDescription(error); 
       NSLog(@"Failed to load font: %@", errorDescription); 
       CFRelease(errorDescription); 
      } 

      CFRelease(font); 
     } 

     CFRelease(provider); 
    } 
} 
+0

Sie brauchen '#import ' in der Lage sein, 'CTFontManagerRegisterGraphicsFont' – MrTristan

10

Hier ist meine Version von John Antwort, die zeigt, wie die Funktion aufgerufen werden, wenn Sie viele Schriftarten haben

import Foundation 

extension UIFont { 

    @nonobjc static var loadAllFontsDO: dispatch_once_t = 0 

    class func initialsAvatarFont() -> UIFont { 
     loadAllFonts() 
     if let retval = UIFont(name: "MyFontName", size: kInitialsAvatarFontSize) { 
      return retval; 
     } else { 
      return UIFont.systemFontOfSize(kInitialsAvatarFontSize) 
     } 
    } 

    class func loadAllFonts() { 
     dispatch_once(&loadAllFontsDO) {() -> Void in 
      registerFontWithFilenameString("thefontfilename.ttf", bundleIdentifierString: "nameOfResourceBundleAlongsideTheFrameworkBundle") 
      // Add more font files here as required 
     } 
    } 

    static func registerFontWithFilenameString(filenameString: String, bundleIdentifierString: String) { 
     let frameworkBundle = NSBundle(forClass: AnyClassInYourFramework.self) 
     let resourceBundleURL = frameworkBundle.URLForResource(bundleIdentifierString, withExtension: "bundle") 
     if let bundle = NSBundle(URL: resourceBundleURL!) { 
      let pathForResourceString = bundle.pathForResource(filenameString, ofType: nil) 
      let fontData = NSData(contentsOfFile: pathForResourceString!) 
      let dataProvider = CGDataProviderCreateWithCFData(fontData) 
      let fontRef = CGFontCreateWithDataProvider(dataProvider) 
      var errorRef: Unmanaged<CFError>? = nil 

      if (CTFontManagerRegisterGraphicsFont(fontRef!, &errorRef) == false) { 
       NSLog("Failed to register font - register graphics font failed - this font may have already been registered in the main bundle.") 
      } 
     } 
     else { 
      NSLog("Failed to register font - bundle identifier invalid.") 
     } 
    } 
} 
+0

Dank für diese nette Lösung. Eine Frage beim Übergeben der bundleIdentifierString, was ist mit "nameOfResourceBundleAlongsideTheFrameworkBundle" gemeint? Können Sie ein Beispiel geben? Bin dankbar! Danke –

+0

@DaisyR. Es ist lange her, seit ich das geschrieben habe, aber: Diese Lösung ist für den Fall, dass Sie ein Framework und ein Ressourcenbündel daneben erstellen. Mit anderen Worten, Sie geben einige andere Entwickler-2-Dateien, 'foo.framework' und' foo.bundle'. Der NameOfResourceBundle ... bezieht sich auf 'foo.bundle' – xaphod

+0

Danke, leider hat dies in meinem Fall nicht funktioniert und in Swift 3 ist displig_once nicht vorhanden, aber wahrscheinlich mit einer Erweiterung ist es machbar. Ich verstehe die Notwendigkeit für diesen Block jedoch nicht zum Laden von Schriftarten. Ich weiß, das ist die Lösung für, wenn Sie ein Framework erstellen, das ist, wie ich zu diesem Beitrag kam :) –

2

Ich dachte, ich meine Antwort teilen würde als Gut. Mein Projekt ist wie so ein:

  • Haupt iOS App (Swift)

    • Dynamische Framework (Obj-C)

      • Fonts.bundle (ein Bündel mit allen die Schriftarten innen)

      • UIFont Kategorien

      • NSBundle Kategorien

      • Andere Framework-Klassen

    • App Classes (Viewcontrollers, Models, Coredata, etc ...)

Mein Ziel war es zu sein Die Hauptanwendung kann eine einzige Methode auf dem dynamischen Framework aufrufen, um Zeichensätze zu laden, ohne dass die Datei Info.plist geändert oder die Schriftartdateien hinzugefügt werden müssen. Bündel zum Hauptziel.

@import CoreText; 

@implementation NSBundle (Fonts) 

+ (NSBundle *)fontsBundle { 
    // The only way I could find to do this is to hard code the sub-path. Using pathForResource doesn't seem to find Fonts.bundle, nor its contents\ 
    // This way the host app doesn't need to copy Fonts.bundle 
    NSString *path = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Frameworks/<YourFrameworkName>.framework/Fonts.bundle"]; 
    NSBundle *bundle = [NSBundle bundleWithPath:path]; 
    if (bundle == nil) { 
     NSLog(@"Warning: Fonts.bundle could not be loaded. Have you included it in your target?"); 
    } 
    return bundle; 
} 

- (BOOL)loadFonts { 

    NSArray<NSString *> *names = @[ 
     @"GothamRnd-Bold", 
     @"GothamRnd-BoldItal", 
     @"GothamRnd-Book", 
     @"GothamRnd-BookItal", 
     @"GothamRnd-Light", 
     @"GothamRnd-LightItal", 
     @"GothamRnd-MedItal", 
     @"GothamRnd-Medium", 
    ]; 

    __block NSInteger failCounter = 0; 
    [names enumerateObjectsUsingBlock:^(id _Nonnull name, NSUInteger idx, BOOL *_Nonnull stop) { 
     NSString *fontPath = [self pathForResource:name ofType:@"otf"]; 
     NSData *inData = [NSData dataWithContentsOfFile:fontPath]; 
     CFErrorRef error; 
     CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData); 
     CGFontRef font = CGFontCreateWithDataProvider(provider); 

     if (!CTFontManagerRegisterGraphicsFont(font, &error)) { 
      if (error) { 
       NSLog(@"Failed to load font at path: %@", fontPath); 
       failCounter++; 
      } 
      CFStringRef errorDescription = CFErrorCopyDescription(error); 
      NSLog(@"Failed to load font: %@", errorDescription); 
      CFRelease(errorDescription); 
     } 
     CFRelease(font); 
     CFRelease(provider); 

    }]; 

    return failCounter == 0; 
} 

@end 

Der einzige Nachteil in diesem Code ist, Sie müssen den Pfad zu den Fonts.bundle hart Code. Ich konnte keine Kombination von NSBundle-Methoden erhalten, um die Fonts.bundle-Datei automatisch zu finden.Zum Beispiel, wie dies keine Verfahren würde einen Weg zurück:

NSString *pathToBundle = [[NSBundle mainBundle] pathForResource:@"Fonts" ofType:@"bundle"]; 
NSString *pathToFont = [[NSBundle mainBundle] pathForResource:@"MyFont" ofType:@"ttf"]; 

Abgesehen von der harten Codierung (das wird sich nie ändern), ist dies für mich zu arbeiten, obwohl gut genug. Ich kann jetzt alle meine Client-Apps problemlos aufbereiten.

+0

Ich verstehe nicht Ihre "fontsBundle" -Methode. Was nennt das? – picciano

+0

Beabsichtigt, so aufgerufen zu werden: if ([[NSBundle fontsBundle] loadFonts] == NO) {/ * error * /} – VaporwareWolf

Verwandte Themen