Xcode 12b1 &Swift 包:自定义字体 [英] Xcode 12b1 & Swift Packages: Custom Fonts

查看:25
本文介绍了Xcode 12b1 &Swift 包:自定义字体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经能够成功地使用 Xcode 12b1 和 Swift 5.3 在 Swift 包中发布一些图像和资产目录.在 Swift 包中使用自定义 .ttf 文件时,我没有那么幸运.

我正在清单中加载一个 .ttf 文件,如下所示:

.target(名称:最佳包装",依赖项:[],资源: [.copy("Resources/Fonts/CustomFont.ttf"),.process("Resources/Colors.xcassets")]),

而且我注意到 SwiftUI 中的 Font 类型没有初始化器来包含来自模块的资产.例如,这有效:

static var PrimaryButtonBackgroundColor: SwiftUI.Color {颜色(组件/按钮/背景",包:.module)}

但是,无法指定字体的来源.我希望将它加载到模块中会将它发送到目标中以供使用,但没有这样的运气:

static var PrimaryButtonFont: Font {Font.custom(自定义字体",大小:34)}

这不会按预期加载字体.我正在研究使用 CoreText api 尝试诱使它加载,但我觉得应该有更简单的方法.有什么建议吗?

更新

仍然没有成功,但我能够证明字体确实在模块内

我编写了一个从模块中获取可用字体 URL 的方法,如下所示:

 static func fontNames() ->[网址] {让 bundle = Bundle.module让文件名 = [自定义字体"]return filenames.map { bundle.url(forResource: $0, withExtension: "ttf")!}}

在运行时调用此方法并打印结果会产生以下结果:

字体名称:[file:///Users/davidokun/Library/Developer/CoreSimulator/Devices/AFE4ADA0-83A7-46AE-9116-7870B883DBD3/data/Containers/Bundle/Application/800AE766-FB60-4AFD-B57A-0E9F3EACCDB2/BestPackageTesting.app/BestPackage_BestPackage.bundle/CustomFont.ttf]

然后我尝试使用以下方法注册在运行时使用的字体:

扩展 UIFont {静态功能寄存器(来自网址:网址){守卫让 fontDataProvider = CGDataProvider(url: url as CFURL) else {打印(无法获得对字​​体数据提供者的引用")返回}守卫让字体 = CGFont(fontDataProvider) else {打印(无法从核心图形中获取字体")返回}变量错误:非托管?守卫 CTFontManagerRegisterGraphicsFont(font, &error) else {打印(错误注册字体:\(error.debugDescription)")返回}}}

当我这样称呼它时:

fontNames().forEach { UIFont.register(from: $0) }

我收到此错误:

error registering font: Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=105 "Could not register the CGFont '<CGFont (0x600000627a00): CustomFont>'" UserInfo={NSDescription=无法注册 CGFont '', CTFailedCGFont=}))

欢迎提出更多想法.

解决方案

我设法使用 SPM 导入自定义字体,使用此 SO 答案来帮助

MyFonts.swift 中,我执行以下操作:

import Foundation//这很重要记得导入 Foundationpublic let fontBundle = Bundle.module

这允许我访问包外的 Bundle.

接下来我将包添加到我的项目中.这是一个带有 AppDelegate 的 SwiftUI 项目.

  • 导入我的字体
  • didFinishLaunchingWithOptions 中检查字体文件是否可用(可选)
  • 使用 UIFont 的扩展添加字体.
  • 打印字体以检查它们是否已安装(可选)

这是我的 AppDelegate:

导入 UIKit导入我的字体@UIApplicationMain类 AppDelegate: UIResponder, UIApplicationDelegate {func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) ->布尔{//这会打印出存储在 MyFont 包中的文件//这样做只是为了检查字体是否确实在包中如果让文件 = 尝试?FileManager.default.contentsOfDirectory(atPath: fontBundle.bundlePath ){对于文件中的文件 {打印(文件)}}//这将注册字体_ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Medium", fontExtension: "ttf")_ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Bold", fontExtension: "ttf")_ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Light", fontExtension: "ttf")_ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Regular", fontExtension: "ttf")_ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Retina", fontExtension: "ttf")//这会打印出所有可用的字体,您应该注意到您的自定义字体出现在此列表中用于 UIFont.familyNames.sorted() {让名称 = UIFont.fontNames(forFamilyName: family)打印(家庭:\(家庭)字体名称:\(名称)")}返回真}//MARK: UISceneSession 生命周期func application(_ application: UIApplication, configurationForConnecting connectedSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->UISceneConfiguration {返回 UISceneConfiguration(名称:默认配置",sessionRole:connectingSceneSession.role)}func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {}}//此扩展名取自此 SO 答案 https://stackoverflow.com/a/36871032/5508175扩展 UIFont {static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) ->布尔{守卫让 fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {FatalError("找不到字体\(fontName)")}守卫让 fontDataProvider = CGDataProvider(url: fontURL as CFURL) else {致命错误(无法从字体\(fontName)加载数据")}守卫让字体 = CGFont(fontDataProvider) else {致命错误(无法从数据创建字体")}变量错误:非托管?让成功 = CTFontManagerRegisterGraphicsFont(font, &error)守卫成功其他{打印(注册字体时出错:可能已经注册了.")返回假}返回真}}

然后在您的 ContentView 中,您可以执行以下操作:

导入 SwiftUI结构内容视图:查看{var主体:一些视图{VStack(间距:20){文本(你好旧金山")Text("Hello FiraCode Medium").font(Font.custom("FiraCode-Medium", size: 16))Text("Hello FiraCode Bold").font(Font.custom("FiraCode-Bold", size: 16))Text("Hello FiraCode Light").font(Font.custom("FiraCode-Light", size: 16))Text("Hello FiraCode Regular").font(Font.custom("FiraCode-Regular", size: 16))Text("Hello FiraCode Retina").font(Font.custom("FiraCode-Retina", size: 16))}}}

结果如下:


注意事项

我还没有在一个完整的 SwiftUI 应用程序中尝试过这个,但是你可以按照显示的教程 此处 了解如何添加 AppDelegate(如果您没有 AppDelegate).

显然,fontBundle 中文件的打印和安装的字体是可选的.它们仅用于调试和确保您拥有正确的字体名称 文件名可能与您必须用于显示字体的字体名称大不相同.请参阅我关于添加自定义字体的 SO 帖子:


更新

我想知道是否有可能创建一个包含在包中的函数并调用它来加载字体.显然是.

我将 MyFonts.swift 更新为以下内容:

导入基础导入 UIKit公共功能注册字体(){_ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Medium", fontExtension: "ttf")_ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Bold", fontExtension: "ttf")_ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Light", fontExtension: "ttf")_ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Regular", fontExtension: "ttf")_ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Retina", fontExtension: "ttf")}扩展 UIFont {static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) ->布尔{守卫让 fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {FatalError("找不到字体\(fontName)")}守卫让 fontDataProvider = CGDataProvider(url: fontURL as CFURL) else {致命错误(无法从字体\(字体名称)加载数据")}守卫让字体 = CGFont(fontDataProvider) else {致命错误(无法从数据创建字体")}变量错误:非托管?让成功 = CTFontManagerRegisterGraphicsFont(font, &error)守卫成功其他{打印(注册字体时出错:可能已经注册了.")返回假}返回真}}

这意味着我可以从 AppDelegate 中删除扩展名,而且我不必像在调用 registerFonts()

之前那样在 AppDelegate 中注册每种字体

所以我的 didFinishLaunchingWithOptions 现在看起来像这样:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) ->布尔{//这将注册字体注册字体()返回真}

请记住,您仍然需要导入您的包.

I have been able to ship some image and asset catalogs in a Swift package with success using Xcode 12b1 and Swift 5.3. I am not having so much luck with using a custom .ttf file in a Swift Package.

I am loading a .ttf file in the manifest like so:

.target(
  name: "BestPackage",
  dependencies: [],
  resources: [
    .copy("Resources/Fonts/CustomFont.ttf"),
    .process("Resources/Colors.xcassets")
  ]
),

And I noticed that there's no initializer on the Font type in SwiftUI to include an asset from a module. For example, this works:

static var PrimaryButtonBackgroundColor: SwiftUI.Color {
  Color("Components/Button/Background", bundle: .module)
}

However, there's no way to specify where a font is coming from. I was hoping that loading it into the module would emit it into the target for use, but no such luck:

static var PrimaryButtonFont: Font {
  Font.custom("CustomFont", size: 34)
}

This does not load the font as expected. I'm investigating using a CoreText api to try and trick it into loading, but I feel like there should be an easier way. Any advice?

Update

Still no success but I was able to prove that the font is indeed inside the module

I wrote a method to get available font URLs from the module like so:

  static func fontNames() -> [URL] {
    let bundle = Bundle.module
    let filenames = ["CustomFont"]
    return filenames.map { bundle.url(forResource: $0, withExtension: "ttf")! }
  }

Calling this method at runtime and printing the result yields this:

font names: [file:///Users/davidokun/Library/Developer/CoreSimulator/Devices/AFE4ADA0-83A7-46AE-9116-7870B883DBD3/data/Containers/Bundle/Application/800AE766-FB60-4AFD-B57A-0E9F3EACCDB2/BestPackageTesting.app/BestPackage_BestPackage.bundle/CustomFont.ttf]

I then tried to register the font for use in the runtime with the following method:

extension UIFont {
  static func register(from url: URL) {
    guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
      print("could not get reference to font data provider")
      return
    }
    guard let font = CGFont(fontDataProvider) else {
      print("could not get font from coregraphics")
      return
    }
    var error: Unmanaged<CFError>?
    guard CTFontManagerRegisterGraphicsFont(font, &error) else {
      print("error registering font: \(error.debugDescription)")
      return
    }
  }
}

When I call it like so:

fontNames().forEach { UIFont.register(from: $0) }

I get this error:

error registering font: Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=105 "Could not register the CGFont '<CGFont (0x600000627a00): CustomFont>'" UserInfo={NSDescription=Could not register the CGFont '<CGFont (0x600000627a00): CustomFont>', CTFailedCGFont=<CGFont (0x600000627a00): CustomFont>}))

Any more ideas are welcome.

解决方案

I managed to import custom fonts using SPM, using this SO answer to help https://stackoverflow.com/a/36871032/5508175

Here is what I did. Create your package and add your fonts. Here is my Package.swift

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "MyFonts",
    products: [
        .library(
            name: "MyFonts",
            targets: ["MyFonts"]),
    ],
    dependencies: [
    ],
    targets: [

        .target(
            name: "MyFonts",
            dependencies: [],
            resources: [.process("Fonts")]),
        .testTarget(
            name: "MyFontsTests",
            dependencies: ["MyFonts"]),
    ]
)

Here is my folder structure. I have all of my fonts contained in a folder called Fonts.

Inside MyFonts.swift I do the following:

import Foundation // This is important remember to import Foundation

public let fontBundle = Bundle.module

This allows me access to the Bundle outside of the package.

Next I added the package to my project. It is a SwiftUI project with an AppDelegate.

  • import MyFonts
  • In didFinishLaunchingWithOptions check to see if the font files are available (optional)
  • Add the fonts using the extension to UIFont.
  • Print out the fonts to check that they are installed (optional)

So here is my AppDelegate:

import UIKit
import MyFonts

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // This prints out the files that are stored in the MyFont bundle
        // Just doing this to check that the fonts are actually in the bundle
        if let files = try? FileManager.default.contentsOfDirectory(atPath: fontBundle.bundlePath ){
            for file in files {
                print(file)
            }
        }

        // This registers the fonts
        _ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Medium", fontExtension: "ttf")
        _ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Bold", fontExtension: "ttf")
        _ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Light", fontExtension: "ttf")
        _ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Regular", fontExtension: "ttf")
        _ = UIFont.registerFont(bundle: fontBundle, fontName: "FiraCode-Retina", fontExtension: "ttf")

        // This prints out all the fonts available you should notice that your custom font appears in this list
        for family in UIFont.familyNames.sorted() {
            let names = UIFont.fontNames(forFamilyName: family)
            print("Family: \(family) Font names: \(names)")
        }

        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {}
}

// This extension is taken from this SO answer https://stackoverflow.com/a/36871032/5508175
extension UIFont {
    static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) -> Bool {

        guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {
            fatalError("Couldn't find font \(fontName)")
        }

        guard let fontDataProvider = CGDataProvider(url: fontURL as CFURL) else {
            fatalError("Couldn't load data from the font \(fontName)")
        }

        guard let font = CGFont(fontDataProvider) else {
            fatalError("Couldn't create font from data")
        }

        var error: Unmanaged<CFError>?
        let success = CTFontManagerRegisterGraphicsFont(font, &error)
        guard success else {
            print("Error registering font: maybe it was already registered.")
            return false
        }

        return true
    }
}

Then in you ContentView you can do something like this:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("Hello San Francisco")
            Text("Hello FiraCode Medium").font(Font.custom("FiraCode-Medium", size: 16))
            Text("Hello FiraCode Bold").font(Font.custom("FiraCode-Bold", size: 16))
            Text("Hello FiraCode Light").font(Font.custom("FiraCode-Light", size: 16))
            Text("Hello FiraCode Regular").font(Font.custom("FiraCode-Regular", size: 16))
            Text("Hello FiraCode Retina").font(Font.custom("FiraCode-Retina", size: 16))
        }
    }
}

Which gives the following result:


Caveats

I haven't tried this in a fully SwiftUI app, but you can follow the tutorial shown here on how to add an AppDelegate if you don't have one.

Obviously the printing of the files in the fontBundle and the fonts that are installed are optional. They are just useful for debugging and for making sure that you have the correct font name The filename can differ quite considerably from the font name that you have to use to display the font. See my SO post about adding custom fonts:


Update

I wondered if it was possible to create a function that was contained in the package and calling that would load the fonts. Apparently it is.

I updated to MyFonts.swift to the following:

import Foundation
import UIKit

public func registerFonts() {
    _ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Medium", fontExtension: "ttf")
    _ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Bold", fontExtension: "ttf")
    _ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Light", fontExtension: "ttf")
    _ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Regular", fontExtension: "ttf")
    _ = UIFont.registerFont(bundle: .module, fontName: "FiraCode-Retina", fontExtension: "ttf")
}

extension UIFont {
    static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) -> Bool {

        guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {
            fatalError("Couldn't find font \(fontName)")
        }

        guard let fontDataProvider = CGDataProvider(url: fontURL as CFURL) else {
            fatalError("Couldn't load data from the font \(fontName)")
        }

        guard let font = CGFont(fontDataProvider) else {
            fatalError("Couldn't create font from data")
        }

        var error: Unmanaged<CFError>?
        let success = CTFontManagerRegisterGraphicsFont(font, &error)
        guard success else {
            print("Error registering font: maybe it was already registered.")
            return false
        }

        return true
    }
}

This meant that I could remove the extension from the AppDelegate and I don't have to register each font in the AppDelegate like I did before I just call registerFonts()

So my didFinishLaunchingWithOptions now looks like this:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // This registers the fonts
    registerFonts()

    return true
}

Remember that you still have to import your package.

这篇关于Xcode 12b1 &amp;Swift 包:自定义字体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆