我将自定义字体添加到了一个框架中。我按照所有步骤进行了操作,但它没有起作用。
我可以在界面生成器(Interface Builder)中设置字体,但在构建项目时,模拟器/设备上并没有展示这个字体。
我将自定义字体添加到了一个框架中。我按照所有步骤进行了操作,但它没有起作用。
我可以在界面生成器(Interface Builder)中设置字体,但在构建项目时,模拟器/设备上并没有展示这个字体。
我来晚了一点,但是我采用了PetahChristian的解决方案,并以扩展的形式创建了一个Swift版本,它对我很有效。 我发现当您尝试使用常规方法使用字体名称和大小获取字体时,它总是在主束中查找字体文件,并且没有可以接受包标识符作为参数的方法。 如果苹果能够提供这个功能就好了。
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")!
)
CTFontManagerRegisterFontsForURL
代替CTFontManagerRegisterGraphicsFont
,让系统处理文件内容的加载到内存中。 - Koen....RegisterFontsForURL
和...RegisterFontURLs
是可用的,应该被使用(正如在...GraphicsFont
的文档中指定的那样)。请给底部提到它的答案点赞。 - itMaxencestatic func registerFonts() {
let fonts = Bundle(for:
OrientationMonitor.self).urls(forResourcesWithExtension: "ttf", subdirectory: nil)
fonts?.forEach({ url in
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
})
}
确保将OrientationMonitor
替换为您框架中存在的类。
let fonts = Bundle.main.urls(...)
。 - tiriteaCTFontManagerRegisterFontURLs(...)
方法来替代 forEach
。 - itMaxence这是我对John答案的版本,展示了如何在有大量字体的情况下调用该函数
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.")
}
}
}
foo.framework
和foo.bundle
。nameOfResourceBundle...指的是foo.bundle
。 - xaphodSwift 4:
这可能是一个旧的线程,但已经更新了@xaphod,因为所有静态和全局变量都是使用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.")
}
}
}
然后你可以在 appDelegate 中调用 UIFont.loadAllfont
我在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
CTFontManagerRegisterGraphicsFont(_:_:)
的文档中可以看出,由文件支持的字体应该使用CTFontManagerRegisterFontsForURL(_:_:_:)
进行注册。由于这个示例是从文件加载字体,我认为这不是使用此API的预期方式。 - B Roy Dawson您可以通过在动态框架中实现+load
方法来加载和使用捆绑的自定义字体。
在load
方法中,您可以定位存储在 bundle 中的字体,然后注册它们。这样就可以在应用程序中使用它们,而不必在主项目中指定它们。
+ (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);
}
}
#import <CoreText/CoreText.h>
才能使用CTFontManagerRegisterGraphicsFont
。 - MrTristan我在 UIFont
上创建了一个扩展,将注册在束中找到的所有字体(指定类型的字体)。请注意,由于这会注册字体,因此无需在框架的 Info.plist 中包括“应用程序提供的字体”。
extension UIFont {
private static var fontsRegistered: Bool = false
static func registerFontsIfNeeded() {
guard
!fontsRegistered,
let fontURLs = someBundle?.urls(forResourcesWithExtension: "otf", subdirectory: nil)
else { return }
fontURLs.forEach({ CTFontManagerRegisterFontsForURL($0 as CFURL, .process, nil) })
fontsRegistered = true
}
}
确保在尝试创建自定义字体实例之前调用 registerFontsIfNeeded()
。例如:
...
registerFontsIfNeeded()
let myCustomFont = UIFont(name: "My-Custom-Font-Name", size: 20)
...
...RegisterFontURLs
函数,是吧?无论如何,回答很好。 - itMaxence我找到了一种非常简单易懂的注册字体的方法,这里没有提到:
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 {}
UIFontPickerViewController
一起使用,而使用CTFontManagerRegisterGraphicsFont
注册的字体由于某些原因不会出现在列表中。 - Jordan H鉴于现在可以直接将资源包含在框架包中,我能够使用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")
}
最终,我需要在使用该框架的应用程序的AppDelegate.swift
中的didFinishLaunchingWithOptions
方法中调用loadMyFonts
方法。
主iOS应用程序(Swift)
动态框架(Obj-C)
Fonts.bundle(包含所有字体的包)
UIFont类别
NSBundle类别
其他框架类
应用程序类(ViewControllers,Models,CoreData等)
@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"];
除了硬编码(永远不会改变)之外,这个对我来说已经足够好了。现在我可以轻松地为所有客户端应用进行界面设计。
CTFontManagerRegisterGraphicsFont
不应该是使用的类,它甚至在自己的文档中告诉你不要使用它。请使用底部基于CTFontManagerRegisterFontsForURL
的答案。 - itMaxence