2015-05-28 10 views
21

Ho aggiunto un carattere personalizzato a un framework. Ho seguito tutti i passaggi, ma non funziona.Xcode: utilizzo di caratteri personalizzati all'interno di Dynamic framework

Sono in grado di impostare il carattere in Interface Builder, ma quando costruisco il progetto non mostra questo carattere sul simulatore/dispositivo.

+0

Hai provato il metodo nella mia risposta? L'ho usato in alcune app che utilizzano font personalizzati in bundle con un framework dinamico, senza la necessità di aggiungere i font al progetto principale. –

risposta

13

Io sono qui un po 'tardi, ma ho preso la soluzione di PetahChristian e ha creato una versione di Swift, sotto forma di una proroga. Questo sta funzionando per me. Ho scoperto che quando si tenta di ottenere un font usando il nome di un font e una dimensione usando il modo normale in cui appare sempre nel bundle principale per il file di font, e non esiste un metodo che accetta un identificatore di bundle come parametro. Sarebbe bello se Apple ne facesse uno.

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 buona modifica –

6

È possibile caricare e utilizzare caratteri personalizzati in bundle dal framework dinamico implementando il metodo + load nel framework.

Nel metodo load, si individuano i caratteri nel pacchetto, quindi li si registra. Questo li rende disponibili per l'app, senza doverli specificare nel progetto principale.

+ (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

avrai bisogno di '#import ' per poter usare 'CTFontManagerRegisterGraphicsFont' – MrTristan

10

Ecco la mia versione della risposta di Giovanni, che mostra come chiamare la funzione se si dispone di un sacco di tipi di carattere

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

grazie per questa bella soluzione. Una domanda quando si passa bundleIdentifierString, cosa si intende per "nameOfResourceBundleAlongsideTheFrameworkBundle"? Puoi fornire un esempio. Apprezzalo! Grazie –

+0

@DaisyR. È passato molto tempo da quando ho scritto questo, ma: questa soluzione è per quando si sta costruendo un framework e un insieme di risorse al suo fianco. In altre parole si stanno dando altri 2 file per sviluppatori, 'foo.framework' e' foo.bundle'. Il nomeOfResourceBundle ... si riferisce a 'foo.bundle' – xaphod

+0

Grazie, Purtroppo non ha funzionato nel mio caso e in Swift 3 dispatch_once non esiste, ma probabilmente con un'estensione è fattibile. Non capisco la necessità di questo blocco anche se per il caricamento di caratteri. So che questa è la soluzione per quando costruisci un framework, ecco come sono arrivato a questo post :) –

2

ho pensato di condividere la mia risposta come bene. Il mio progetto è impostato in questo modo:

  • principale iOS App (Swift)

    • quadro dinamico (Obj-C)

      • Fonts.bundle (un bundle con tutte le i caratteri interni)

      • Categorie UIFont

      • categorie NSBundle

      • Altre classi del framework

    • App classi (ViewControllers, Modelli, CoreData, ecc ...)

il mio obiettivo era di essere in grado di fare in modo che l'app principale chiami un singolo metodo sul framework dinamico per caricare i font senza dover modificare Info.plist o aggiungere i file di font/fascio al bersaglio principale.

@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 

L'unico inconveniente in questo codice è che è necessario codificare il percorso del file Fonts.bundle. Non è stato possibile ottenere alcuna combinazione di metodi NSBundle per individuare automaticamente il file Fonts.bundle.Per esempio nessun metodo come questo sarebbe restituire un percorso:

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

A parte la codifica (che non cambierà mai) del disco, questo sta lavorando per me abbastanza bene però. Ora posso applicare facilmente tutte le mie app client.

+0

Non sto capendo il tuo metodo "fontBundle". Cosa lo chiama? – picciano

+0

Destinato a essere chiamato in questo modo: if ([[NSBundle fontsBundle] loadFonts] == NO) {/ * error * /} – VaporwareWolf

Problemi correlati