Xcode: Using custom fonts inside Dynamic framework

前端 未结 9 1562
再見小時候
再見小時候 2020-12-04 17:52

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

9条回答
  •  旧巷少年郎
    2020-12-04 18:37

    I thought I'd share my answer as well. My project is set up like so:

    • Main iOS App (Swift)

      • Dynamic Framework (Obj-C)

        • Fonts.bundle (a bundle with all the fonts inside)

        • UIFont categories

        • NSBundle categories

        • Other framework classes

      • App Classes (ViewControllers, Models, CoreData, etc...)

    My goal was to be able to have the main app call a single method on the dynamic framework to load fonts without the need for altering the Info.plist or adding the font files/bundle to the main target.

    @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/.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 *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
    

    The only bummer in this code is you have to hard code the path to the Fonts.bundle. I couldn't get any combination of NSBundle methods to locate the Fonts.bundle file automatically. For instance no methods like this would return a path:

    NSString *pathToBundle = [[NSBundle mainBundle] pathForResource:@"Fonts" ofType:@"bundle"];
    NSString *pathToFont = [[NSBundle mainBundle] pathForResource:@"MyFont" ofType:@"ttf"];
    

    Aside from the hard coding (which will never change), this is working for me well enough though. I can now skin all of my client apps easily.

提交回复
热议问题