Я добавил пользовательский шрифт в фреймворк. Я выполнил все шаги, но это не сработало.
Я могу установить шрифт в Interface Builder, но когда я создаю проект, он не отображает этот шрифт на симуляторе/устройстве.
Я добавил пользовательский шрифт в фреймворк. Я выполнил все шаги, но это не сработало.
Я могу установить шрифт в Interface Builder, но когда я создаю проект, он не отображает этот шрифт на симуляторе/устройстве.
Я немного опоздал, но я выбрал решение PetahChristian и создал версию Swift в виде расширения. Это работает для меня. Я обнаружил, что когда вы пытаетесь получить шрифт, используя имя шрифта и размер, используя обычный способ, которым он всегда выглядит в главном пакете для файла шрифта, и не существует метода, который принимает идентификатор пакета в качестве параметра. Было бы хорошо, если бы Apple сделала один.
Swift:
public extension UIFont {
public static func jbs_registerFont(withFilenameString 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 font = CGFont(dataProvider) else {
print("UIFont+: Failed to register font - font could not be loaded.")
return
}
var errorRef: Unmanaged<CFError>? = nil
if (CTFontManagerRegisterGraphicsFont(font, &errorRef) == false) {
print("UIFont+: Failed to register font - register graphics font failed - this font may have already been registered in the main bundle.")
}
}
}
Пример использования:
UIFont.jbs_registerFont(
withFilenameString: "Boogaloo-Regular.ttf",
bundle: Bundle(identifier: "com.JBS.JBSFramework")!
)
Здесь моя версия Джона отвечает, показывая, как вызвать функцию, если у вас много шрифтов
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.")
}
}
}
Вы можете загружать и использовать встраиваемые пользовательские шрифты из своей динамической структуры, внедряя метод + 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);
}
}
Свифт 4:
Возможно, это старый поток, но он обновил @xaphod для swift 4, поскольку все статические и глобальные переменные лениво инициализируются с использованием dispatch_once.
extension UIFont {
// load framework font in application
public static let loadAllFonts: () = {
registerFontWith(filenameString: "SanFranciscoText-Regular.otf", bundleIdentifierString: "Fonts")
registerFontWith(filenameString: "SanFranciscoText-Medium.otf", bundleIdentifierString: "Fonts")
registerFontWith(filenameString: "SanFranciscoText-Semibold.otf", bundleIdentifierString: "Fonts")
registerFontWith(filenameString: "SanFranciscoText-Bold.otf", bundleIdentifierString: "Fonts")
registerFontWith(filenameString: "SanFranciscoText-LightItalic.otf", bundleIdentifierString: "Fonts")
}()
//MARK: - Make custom font bundle register to framework
static func registerFontWith(filenameString: String, bundleIdentifierString: String) {
let frameworkBundle = Bundle(for: MSAlertController.self)
let resourceBundleURL = frameworkBundle.url(forResource: bundleIdentifierString, withExtension: "bundle")
if let url = resourceBundleURL, let bundle = Bundle(url: url) {
let pathForResourceString = bundle.path(forResource: filenameString, ofType: nil)
if let fontData = NSData(contentsOfFile: pathForResourceString!), let dataProvider = CGDataProvider.init(data: fontData) {
let fontRef = CGFont.init(dataProvider)
var errorRef: Unmanaged<CFError>? = nil
if (CTFontManagerRegisterGraphicsFont(fontRef!, &errorRef) == false) {
print("Failed to register font - register graphics font failed - this font may have already been registered in the main bundle.")
}
}
}
else {
print("Failed to register font - bundle identifier invalid.")
}
}
}
Затем вы можете вызвать UIFont.loadAllfont
внутри appDelegate
Я думал, что тоже поделюсь своим ответом. Мой проект настроен так:
Основное приложение iOS (Swift)
Динамическая структура (Obj-C)
Fonts.bundle(набор со всеми шрифтами внутри)
Категории UIFont
Категории NSBundle
Другие классы инфраструктуры
Классы приложений (ViewControllers, Models, 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"];
Помимо жесткого кодирования (которое никогда не изменится), это работает для меня достаточно хорошо, хотя. Теперь я могу легко скрыть все мои клиентские приложения.
Был в состоянии сделать это с Swift 4, учитывая, что вы можете теперь включать ресурсы непосредственно в комплект фреймворка:
Typography.swift
(в моих рамках)
import Foundation
private class MyDummyClass {}
func loadFontWith(name: String) {
let frameworkBundle = Bundle(for: MyDummyClass.self)
let pathForResourceString = frameworkBundle.path(forResource: name, ofType: "otf")
let fontData = NSData(contentsOfFile: pathForResourceString!)
let dataProvider = CGDataProvider(data: fontData!)
let fontRef = CGFont(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.")
}
}
public func loadMyFonts() {
loadFontWith(name: "ComicSansPro-Regular")
loadFontWith(name: "ComicSansPro-Medium")
loadFontWith(name: "ComicSansPro-Bold")
loadFontWith(name: "ComicSansPro-ExtraBold")
}
Я закончил тем, что требовал, loadMyFonts
метод loadMyFonts
был вызван в приложении, которое использует этот каркас, didFinishLaunchingWithOptions
метод AppDelegate.swift
в AppDelegate.swift
.
Нашел действительно простой и читаемый способ регистрации шрифта, о котором здесь не упоминается:
func registerFont(with fontName: String) {
guard let url = Bundle(for: BundleToken.self).url(forResource: fontName, withExtension: nil),
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil) else {
fatalError("Failed to register font: \(font.fileName)")
}
}
private final class BundleToken {}
Я сделал смесь разных ответов в Swift 4.2, так что подпишитесь на парней, которые его придумали!
import UIKit
import Foundation
extension UIFont {
private class MyDummyClass {}
static func loadFontWith(name: String) {
let frameworkBundle = Bundle(for: MyDummyClass.self)
let pathForResourceString = frameworkBundle.path(forResource: name, ofType: "ttf")
let fontData = NSData(contentsOfFile: pathForResourceString!)
let dataProvider = CGDataProvider(data: fontData!)
let fontRef = CGFont(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.")
}
}
public static let loadMyFonts: () = {
loadFontWith(name: "Exo-Black")
loadFontWith(name: "Exo-Bold")
loadFontWith(name: "Exo-Regular")
}()
}
А затем позвоните в Appdelegate
UIFont.loadMyFonts