2015-05-28 4 views
21

Я добавил пользовательский шрифт в фреймворк. Я выполнил все шаги, но это не сработало.Xcode: использование пользовательских шрифтов внутри динамической структуры

Я могу установить шрифт в Interface Builder, но когда я создаю проект, он не отображает этот шрифт на симуляторе/устройстве.

+0

Пробовал ли вы метод в моем ответе? Я использую его в нескольких приложениях, которые используют пользовательские шрифты в комплекте с динамической структурой без необходимости добавления шрифтов в основной проект. –

ответ

13

Я здесь немного поздно, но я принял решение PetahChristian и создал Swift версию в виде расширения. Это работает для меня. Я обнаружил, что при попытке получить шрифт с использованием имени шрифта и размера, используя обычный способ, который он всегда просматривает в основном пакете для файла шрифта, и нет метода, который принимает идентификатор связки в качестве параметра. Было бы неплохо, если бы Apple сделала это.

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 good edit –

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? Можете привести пример. Ценить это! Thanks –

+0

@DaisyR. Прошло много времени с тех пор, как я написал это, но: это решение для того, когда вы строите инфраструктуру и пакет ресурсов рядом с ним. Другими словами, вы предоставляете некоторые другие файлы разработчиков 2, 'foo.framework' и' foo.bundle'. ИмяOfResourceBundle ... относится к 'foo.bundle' – xaphod

+0

Спасибо, к сожалению, это не работает в моем случае, а в Swift 3 dispatch_once не существует, но, вероятно, с расширением он выполним. Я не понимаю необходимость этого блока, хотя для загрузки шрифтов. Я знаю, что это решение, когда вы строите фреймворк, вот как я попал на этот пост :) –

2

я думал, что я разделю свой ответ как Что ж. Мой проект настроен следующим образом:

  • Главная IOS App (Swift)

    • Dynamic Framework (Obj-C)

      • Fonts.bundle (расслоение со всеми шрифты внутри)

      • Категории UIFont

      • NSBundle категории

      • Другие классы структуры

    • App Классы (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

Смежные вопросы