Xcode:在Dynamic框架内使用自定义字体

问题描述 投票:27回答:8

我在框架中添加了自定义字体。我遵循了所有步骤,但它不起作用。

我能够在Interface Builder中设置字体,但是当我构建项目时,它不会在模拟器/设备上显示此字体。

swift ios8 frameworks
8个回答
25
投票

我在这里有点晚了,但是我采用了PetahChristian的解决方案,并以扩展的形式创建了一个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")!
)

12
投票

这是我的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.")
        }
    }
}

7
投票

您可以通过在框架中实现+ 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);
    }
}

7
投票

斯威夫特4:

这可能是一个旧线程,但已更新@xaphod for 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.")
    }
}
}

然后你可以在appDelegate里面调用UIFont.loadAllfont


2
投票

我以为我也会分享我的答案。我的项目设置如下:

  • 主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"];

除了硬编码(永远不会改变)之外,这对我很有帮助。我现在可以轻松地为所有客户端应用打印。


2
投票

能够使用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方法,该方法在didFinishLaunchingWithOptions中使用此框架的AppDelegate.swift方法。


2
投票

找到一种非常简单易读的注册字体方式,这里没有提到:

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 {}

1
投票

我在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
© www.soinside.com 2019 - 2024. All rights reserved.