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

Posted

技术标签:

【中文标题】我可以在包中嵌入自定义字体并从 ios 框架访问它吗?【英文标题】:Can I embed a custom font in a bundle and access it from an ios framework? 【发布时间】:2013-01-22 00:46:10 【问题描述】:

我正在创建一个带有 bundleios framework,用于打包资源(笔尖、图像、字体),并且我正在尝试嵌入 自定义字体 在捆绑包中,但我无法从框架中加载它,这可能吗?

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

我可以从包中加载笔尖和图像(通过以包名称为前缀),但不能加载字体。有什么想法吗?

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

谢谢

【问题讨论】:

我找到了一个解决方法,(仍然希望有更好的解决方案)。将包含该框架的用户必须在其info.plist 中添加对捆绑中字体的引用:UIAppFonts key=item0 value=BUNDLENAME.bundle/FONT_FILENAME.ttf 非常感谢!你的方式帮助了我 Framework bundle 可以这样获取:NSBundle *bundle = [NSBundle bundleForClass:self.class]; 【参考方案1】:

斯威夫特 3:

首先,不要从 main 访问带有附加路径组件的框架包...从其标识符实例化它。您可以像这样获取字体 URL:

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")! )

我发现有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()
        
    

现在享受注册:

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

【讨论】:

这行得通 - 只需确保字体在文件资源管理器中作为资源加载,而不是像我一开始那样在 Assets.xcassets 文件夹中加载... 很好的答案!这是要走的路。 略有改动。谢谢!【参考方案2】:

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

【讨论】:

【参考方案3】:

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

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) 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;

注意:需要将 CoreText 集成到您的项目中

【讨论】:

不适合我):我得到Thread 1: EXC_BREAKPOlNT (code=EXC_ARM_BREAKPOINT. subcode=0xdefe)。最后我还尝试删除CFReleases,但没有帮助.... 为我工作,您是否查看了经过验证的问题。它提供了指向我实施的解决方案的链接,也许还有更多信息? 需要注意的是,资源名称字符串必须是字体文件的名称,不一定是字体名称。【参考方案4】:

在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
   

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
 

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

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

欢迎您的指正和评论!

【讨论】:

对我来说是NSBundle.mainBundle().privateFrameworksPath,而不是NSBundle.mainBundle().bundlePath 您还应该检查函数CTFontManagerRegisterGraphicsFont 调用的返回值。不是error 参数 你应该强制解开可选项,它们是可选项,因为它们可以为 nil,如果它们为 nil 时应用程序将崩溃。使用guard 语句来解包并提前退出,如果为零,则使用throw【参考方案5】:

为 Swift 4/5 更新并更改为抛出错误而不是返回 Bool。

enum FontLoadingError: Error 
    case fileNotFound
    case unreadableFontData


func loadCustomFont(name: String) throws 
    guard let fontURL = frameworkBundle.url(forResource: name, withExtension: "ttf") else 
        throw FontLoadingError.fileNotFound
    

    guard
        let provider = CGDataProvider(url: fontURL as CFURL),
        let font = CGFont(provider)
    else 
        throw FontLoadingError.unreadableFontData
    

    var cfError: Unmanaged<CFError>?
    CTFontManagerRegisterGraphicsFont(font, &cfError)

    if let error = cfError as? Error 
        throw error
    

【讨论】:

【参考方案6】:

如果您在文件/包中有字体,则可以使用此扩展。

public extension UIFont 

    static func register(from url: URL) throws 
        if !FileManager.default.fileExists(atPath: url.path) 
            throw VError.incorrectFont
        

        var error: Unmanaged<CFError>?

        guard CTFontManagerRegisterFontsForURL(url as CFURL, .process, &error) else 
            throw error!.takeUnretainedValue()
        
    

【讨论】:

【参考方案7】:

@Ali-ABBAS 答案的 Swift 3 版本,也更新为向上包装选项,而不是强制展开。

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

【讨论】:

以上是关于我可以在包中嵌入自定义字体并从 ios 框架访问它吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在iPhone应用程序中嵌入自定义字体吗?

Laravel 在包中使用自定义 USER-Model

我可以在 Android 应用程序中嵌入自定义字体吗?

如何使用静态库传输资源文件(如何将资源包装在包中)?

自定义字体在 iOS 8 中不起作用

如何让 Cordova 通过插件自动嵌入自定义 iOS 框架?