I added custom font to a framework. I followed all the steps, but it doesn\'t work.
I am able to set the font in Interface Builder, but when I build the project it d
I made a mix of different answers in Swift 4.2 so props to the guys who came up with it!
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")
}()
}
And then call in Appdelegate
UIFont.loadMyFonts
Here's my version of John's answer, showing how to call the function if you have lots of fonts
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.")
}
}
}
The following solution allows you to load all fonts with a certain extension automatically:
static func registerFonts() {
let fonts = Bundle(for:
OrientationMonitor.self).urls(forResourcesWithExtension: "ttf", subdirectory: nil)
fonts?.forEach({ url in
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
})
}
Make sure to replace OrientationMonitor
with a class existing in your framework.
Was able to do this with Swift 4, given that you can include resources directly in a framework bundle now:
Typography.swift
(in my framework)
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")
}
I ended up requiring the loadMyFonts
method to be called in the application's that use this framework's didFinishLaunchingWithOptions
method in AppDelegate.swift
.
Swift 4:
This is maybe an old thread but has updated @xaphod for swift 4 as all static and global variables are lazily initialised using 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.")
}
}
}
Then you can call UIFont.loadAllfont
inside the appDelegate
You can load and use bundled custom fonts from your dynamic framework by implementing the +load
method in your framework.
In the load
method, you locate the fonts in the bundle, then register them. This makes them available to the app, without having to specify them in the main project.
+ (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);
}
}