2015-05-28 83 views
21

我將自定義字體添加到框架。我遵循了所有步驟,但不起作用。Xcode:在動態框架內使用自定義字體

我可以在Interface Builder中設置字體,但是當我構建項目時,它不會在模擬器/設備上顯示此字體。

+0

您是否在我的答案中嘗試過該方法?我已經在一些使用與動態框架捆綁的自定義字體的應用中使用它,而不需要將字體添加到主項目中。 –

回答

13

我在這裏有點晚了,但是我拿了PetahChristian的解決方案,並以擴展的形式創建了一個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好編輯 –

6

通過在框架中實現+ load方法,您可以從動態框架加載和使用捆綁的自定義字體。

load方法中,您可以在包中找到字體,然後註冊它們。這使得它們可供應用程序使用,而無需在主項目中指定它們。

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

您需要'#import '才能使用'CTFontManagerRegisterGraphicsFont' – MrTristan

10

這裏是我的版本約翰的回答,展示如何調用該函數,如果你有很多的字體

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

感謝這個不錯的解決方案。傳遞bundleIdentifierString時有一個問題,「nameOfResourceBundleAlongsideTheFrameworkBundle」是什麼意思?你能提供一個例子嗎?欣賞它!謝謝 –

+0

@DaisyR。我寫這篇文章已經很長時間了,但是:這個解決方案適用於在構建框架和資源包的時候。換句話說,你正在給一些其他的開發者2文件,'foo.framework'和'foo.bundle'。 nameOfResourceBundle ...引用'foo.bundle' – xaphod

+0

謝謝,不幸的是,這不工作在我的情況下,在Swift 3 dispatch_once不存在,但可能與擴展它是可行的。我不明白這個塊需要加載字體。我知道這是解決方案,當你正在構建一個框架,這是我如何到這篇文章:) –

2

我想我會分享我的答案的好。我的項目設置,如下所示:

  • 主要iOS應用(SWIFT)

    • 動態框架(對象 - )

      • Fonts.bundle(所有捆綁字體裏面)

      • UIFont類別

      • 一個NSBundle類

      • 其他框架類

    • 應用類(ViewControllers,模型,CoreData,等...)

我的目標是成爲能夠讓主應用程序調用動態框架上的單一方法來加載字體,而無需更改Info.plist或添加字體文件/捆綁到主要目標。

@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 

在這段代碼中,唯一無用的是你必須硬編碼到Fonts.bundle的路徑。我無法獲得任何NSBundle方法的組合來自動查找Fonts.bundle文件。從硬編碼(這永遠不會改變),這是爲我工作不夠好

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

除了,但:比如像這樣沒有方法將返回的路徑。我現在可以輕鬆地清理所有客戶端應用程序。

+0

我不理解你的「fontsBundle」方法。什麼叫這個? – picciano

+0

打算如此調用:if([[NSBundle fontsBundle] loadFonts] == NO){/ * error * /} – VaporwareWolf