我可以在捆绑中嵌入自定义字体并从ios框架访问它吗?

时间:2022-09-25 12:28:27

I'm creating an ios framework with its bundle for packaging ressources (nib, images, fonts) and I'm trying to embed a custom font in the bundle but I'm not able to load it from the framework, is it possible ?

我正在创建一个ios框架及其包装用于打包资源(笔尖,图像,字体),我正在尝试在包中嵌入自定义字体,但我无法从框架中加载它,是否可能?

1) I can localize the font file with this: objc NSString *fontPath = [[NSBundle frameworkBundle] pathForResource:@"MyCustomFont" ofType:@"ttf"]; 2) But I can't get it in my fonts lists: objc NSArray * array = [UIFont familyNames]; I included my font name in the bundle's plist with a "Fonts provided by application", without success, tried also in the app info plist, include it in the framework ressource without success.

1)我可以使用以下方法对字体文件进行本地化:objc NSString * fontPath = [[NSBundle frameworkBundle] pathForResource:@“MyCustomFont”ofType:@“ttf”]; 2)但我无法在我的字体列表中找到它:objc NSArray * array = [UIFont familyNames];我在包的plist中包含了我的字体名称,其中包含“应用程序提供的字体”,但没有成功,也尝试在app info plist中,将其包含在框架资源中但没有成功。

I can load the nib and images from the bundle (by prefixing with the bundle's name) but not for the font. Any thought ?

我可以从包中加载笔尖和图像(通过前缀使用包的名称),但不能加载字体。任何想法 ?

EDIT : I saw the following post : Can I embed a custom font in an iPhone application?, but the question is just "Can I embed a custom font in an iPhone application?" not "Can I embed a custom font in an external framework/bundle ?" It also makes references to a dynamic loading which is interesting but it is using private api, which is not usable solution for a framework.

编辑:我看到以下帖子:我可以在iPhone应用程序中嵌入自定义字体吗?但问题是“我可以在iPhone应用程序中嵌入自定义字体吗?”不是“我可以在外部框架/捆绑包中嵌入自定义字体吗?”它还引用了一个有趣的动态加载,但它使用私有api,这对于框架来说不是可用的解决方案。

Thanks

谢谢

5 个解决方案

#1


16  

This is a new method that lets you load fonts dynamically without putting them in your Info.plist: http://www.marco.org/2012/12/21/ios-dynamic-font-loading

这是一种新方法,可让您动态加载字体,而无需将其放入Info.plist中:http://www.marco.org/2012/12/21/ios-dynamic-font-loading

#2


16  

Here is way I implemented it for my fmk based on the solution provided by "David M." This solution doesn't require to add the reference to the font in the plist.

这是我根据“David M.”提供的解决方案为我的fmk实现的方法。此解决方案不需要在plist中添加对字体的引用。

1) Class that load the font

1)加载字体的类

- (void) loadMyCustomFont{
    NSString *fontPath = [[NSBundle frameworkBundle] pathForResource:@"MyFontFileNameWithoutExtension" ofType:@"ttf"];
    NSData *inData = [NSData dataWithContentsOfFile:fontPath];
    CFErrorRef error;
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
    CGFontRef font = CGFontCreateWithDataProvider(provider);
    if (! CTFontManagerRegisterGraphicsFont(font, &error)) {
        CFStringRef errorDescription = CFErrorCopyDescription(error);
        NSLog(@"Failed to load font: %@", errorDescription);
        CFRelease(errorDescription);
    }
    CFRelease(font);
    CFRelease(provider);
}

2) Category on NSBundle to get access to my bundle

2)NSBundle上的类别可以访问我的包

+ (NSBundle *)frameworkBundle {
    static NSBundle* frameworkBundle = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        NSString* mainBundlePath = [[NSBundle mainBundle] resourcePath];
        NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"MyBundleName.bundle"];
        frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
    });
    return frameworkBundle;
}

Note: require to integrate CoreText in your project

注意:需要在项目中集成CoreText

#3


10  

Swift 3:

斯威夫特3:

Firstly, don't access framework bundle from main with appending path components... Instantiate it from its identifier. You can get font URLs like this:

首先,不要使用附加路径组件从main访问框架包...从其标识符实例化它。您可以获得如下字体网址:

static func fontsURLs() -> [URL] {
    let bundle = Bundle(identifier: "com.company.project.framework")!
    let fileNames = ["Roboto-Bold", "Roboto-Italic", "Roboto-Regular"]
    return fileNames.map({ bundle.url(forResource: $0, withExtension: "ttf")! })
}

And I find it nice to have UIFont extension for registering fonts:

我发现使用UIFont扩展来注册字体很不错:

public extension UIFont {
    public static func register(from url: URL) throws {
        guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
            throw SVError.internal("Could not create font data provider for \(url).")
        }
        let font = CGFont(fontDataProvider)
        var error: Unmanaged<CFError>?
        guard CTFontManagerRegisterGraphicsFont(font, &error) else {
            throw error!.takeUnretainedValue()
        }
    }
}

Now enjoy the registration:

现在享受注册:

do {
    try fontsURLs().forEach({ try UIFont.register(from: $0) })
} catch {
    print(error)
}

#4


4  

In swift, I use the code below :

在swift中,我使用下面的代码:

public class func loadMyCustomFont(name:String) -> Bool{
  let fontPath = self.frameworkBundle().pathForResource(name, ofType: "ttf")!
  let inData = NSData(contentsOfFile:fontPath)
  var error: Unmanaged<CFError>?
  let provider = CGDataProviderCreateWithCFData(inData)
  if let font = CGFontCreateWithDataProvider(provider) {
     CTFontManagerRegisterGraphicsFont(font, &error)
     if error != nil {
       print(error) //Or logged it
       return false
     }


      }
       return true
   }

The frameworkBundle method :

frameworkBundle方法:

class func frameworkBundle() -> NSBundle{
       var bundle = NSBundle()
       var predicate = dispatch_once_t()
       dispatch_once(&predicate) {
          let mainBundlePath = NSBundle.mainBundle().bundlePath
          let frameworkBundlePath = mainBundlePath.stringByAppendingString("/myFramework.framework/")
          bundle = NSBundle(path: frameworkBundlePath)!
       }
       return bundle
 }

Exemple of call : (In my case, i added all fonts in the Fonts folder)

调用的例子:(在我的例子中,我添加了Fonts文件夹中的所有字体)

YouClassName.loadMyCustomFont("Fonts/Roboto-Regular")

Your corrections and remarks are welcome !

欢迎您的更正和评论!

#5


0  

Swift 3 version of @Ali-ABBAS's answer, also updated to up-wrap options instead of force unwrapping.

Swift 3版本@Ali-ABBAS的答案,也更新为up-wrap选项而不是强制解包。

fileprivate func loadCustomFont(name:String) -> Bool{

    guard let fontPath = frameworkBundle.path(forResource: name, ofType: "ttf") else {
        return false
    }

    guard let inData = NSData(contentsOfFile:fontPath) else {
        return false
    }


    guard let provider = CGDataProvider(data: inData) else {
        return false
    }

    let font = CGFont(provider)
    var error: Unmanaged<CFError>?
    CTFontManagerRegisterGraphicsFont(font, &error)
    guard error == nil else {
        print(error) //Or logged it
        return false
    }

    return true
}

#1


16  

This is a new method that lets you load fonts dynamically without putting them in your Info.plist: http://www.marco.org/2012/12/21/ios-dynamic-font-loading

这是一种新方法,可让您动态加载字体,而无需将其放入Info.plist中:http://www.marco.org/2012/12/21/ios-dynamic-font-loading

#2


16  

Here is way I implemented it for my fmk based on the solution provided by "David M." This solution doesn't require to add the reference to the font in the plist.

这是我根据“David M.”提供的解决方案为我的fmk实现的方法。此解决方案不需要在plist中添加对字体的引用。

1) Class that load the font

1)加载字体的类

- (void) loadMyCustomFont{
    NSString *fontPath = [[NSBundle frameworkBundle] pathForResource:@"MyFontFileNameWithoutExtension" ofType:@"ttf"];
    NSData *inData = [NSData dataWithContentsOfFile:fontPath];
    CFErrorRef error;
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
    CGFontRef font = CGFontCreateWithDataProvider(provider);
    if (! CTFontManagerRegisterGraphicsFont(font, &error)) {
        CFStringRef errorDescription = CFErrorCopyDescription(error);
        NSLog(@"Failed to load font: %@", errorDescription);
        CFRelease(errorDescription);
    }
    CFRelease(font);
    CFRelease(provider);
}

2) Category on NSBundle to get access to my bundle

2)NSBundle上的类别可以访问我的包

+ (NSBundle *)frameworkBundle {
    static NSBundle* frameworkBundle = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        NSString* mainBundlePath = [[NSBundle mainBundle] resourcePath];
        NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"MyBundleName.bundle"];
        frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
    });
    return frameworkBundle;
}

Note: require to integrate CoreText in your project

注意:需要在项目中集成CoreText

#3


10  

Swift 3:

斯威夫特3:

Firstly, don't access framework bundle from main with appending path components... Instantiate it from its identifier. You can get font URLs like this:

首先,不要使用附加路径组件从main访问框架包...从其标识符实例化它。您可以获得如下字体网址:

static func fontsURLs() -> [URL] {
    let bundle = Bundle(identifier: "com.company.project.framework")!
    let fileNames = ["Roboto-Bold", "Roboto-Italic", "Roboto-Regular"]
    return fileNames.map({ bundle.url(forResource: $0, withExtension: "ttf")! })
}

And I find it nice to have UIFont extension for registering fonts:

我发现使用UIFont扩展来注册字体很不错:

public extension UIFont {
    public static func register(from url: URL) throws {
        guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
            throw SVError.internal("Could not create font data provider for \(url).")
        }
        let font = CGFont(fontDataProvider)
        var error: Unmanaged<CFError>?
        guard CTFontManagerRegisterGraphicsFont(font, &error) else {
            throw error!.takeUnretainedValue()
        }
    }
}

Now enjoy the registration:

现在享受注册:

do {
    try fontsURLs().forEach({ try UIFont.register(from: $0) })
} catch {
    print(error)
}

#4


4  

In swift, I use the code below :

在swift中,我使用下面的代码:

public class func loadMyCustomFont(name:String) -> Bool{
  let fontPath = self.frameworkBundle().pathForResource(name, ofType: "ttf")!
  let inData = NSData(contentsOfFile:fontPath)
  var error: Unmanaged<CFError>?
  let provider = CGDataProviderCreateWithCFData(inData)
  if let font = CGFontCreateWithDataProvider(provider) {
     CTFontManagerRegisterGraphicsFont(font, &error)
     if error != nil {
       print(error) //Or logged it
       return false
     }


      }
       return true
   }

The frameworkBundle method :

frameworkBundle方法:

class func frameworkBundle() -> NSBundle{
       var bundle = NSBundle()
       var predicate = dispatch_once_t()
       dispatch_once(&predicate) {
          let mainBundlePath = NSBundle.mainBundle().bundlePath
          let frameworkBundlePath = mainBundlePath.stringByAppendingString("/myFramework.framework/")
          bundle = NSBundle(path: frameworkBundlePath)!
       }
       return bundle
 }

Exemple of call : (In my case, i added all fonts in the Fonts folder)

调用的例子:(在我的例子中,我添加了Fonts文件夹中的所有字体)

YouClassName.loadMyCustomFont("Fonts/Roboto-Regular")

Your corrections and remarks are welcome !

欢迎您的更正和评论!

#5


0  

Swift 3 version of @Ali-ABBAS's answer, also updated to up-wrap options instead of force unwrapping.

Swift 3版本@Ali-ABBAS的答案,也更新为up-wrap选项而不是强制解包。

fileprivate func loadCustomFont(name:String) -> Bool{

    guard let fontPath = frameworkBundle.path(forResource: name, ofType: "ttf") else {
        return false
    }

    guard let inData = NSData(contentsOfFile:fontPath) else {
        return false
    }


    guard let provider = CGDataProvider(data: inData) else {
        return false
    }

    let font = CGFont(provider)
    var error: Unmanaged<CFError>?
    CTFontManagerRegisterGraphicsFont(font, &error)
    guard error == nil else {
        print(error) //Or logged it
        return false
    }

    return true
}