在Swift中为iOS字体描述符添加自定义权重

18

看起来这应该是一件基本容易的事情,但出于某种原因它并没有起作用。我在 Stack Overflow 上读了一些类似的帖子,似乎字体特征词典可能存在问题?这是个问题还是我完全忽略了什么?这是我第一次尝试这样的字体处理,所以我想可能是后者。

我不想使用常量或提供一个带有“-bold”变体的字体。我希望对字体重量有精细的控制。

let traits = [UIFontWeightTrait : 1.0]
imgFontDescriptor = UIFontDescriptor(fontAttributes: [UIFontDescriptorNameAttribute: "Helvetica"])
imgFontDescriptor = imgFontDescriptor.fontDescriptorByAddingAttributes([UIFontDescriptorTraitsAttribute:traits])
imgTextFont = UIFont(descriptor: imgFontDescriptor!, size: 24.0)

研究:

iOS文档中指出,使用数字值应该可以工作(我正在运行iOS 9.2):

UIFontWeightTrait(UIFont重量特征)

规格化的权重值作为NSNumber对象。有效值范围为-1.0到1.0。值0.0对应于常规或中等字重。您还可以使用字重常量来指定特定的字重;有关可用常量的列表,请参见字重。自iOS 7.0以后可用。

6个回答

28

如果您使用现有的fontDescriptor(例如从现有的字体中),它可能无法正常工作,因为fontDescriptor.fontAttributes中存在显式的'.name'属性。

对我有效的解决方案(Swift 4):

extension UIFont {
    var bold: UIFont { return withWeight(.bold) }
    var semibold: UIFont { return withWeight(.semibold) }

    private func withWeight(_ weight: UIFont.Weight) -> UIFont {
        var attributes = fontDescriptor.fontAttributes
        var traits = (attributes[.traits] as? [UIFontDescriptor.TraitKey: Any]) ?? [:]

        traits[.weight] = weight

        attributes[.name] = nil
        attributes[.traits] = traits
        attributes[.family] = familyName

        let descriptor = UIFontDescriptor(fontAttributes: attributes)

        return UIFont(descriptor: descriptor, size: pointSize)
    }
}

使用方法:

let baseFont = UIFont(name: "American Typewriter", size: 20)!
let boldFont = baseFont.bold
let semibold = baseFont.semibold

2
这就是我一直在寻找的解决方案。非常有效。 - Daniel
同意!!!这个方法很好用。我真的想不出来这个问题的解决方法。 - Ashish Bansal

16
UIFont(descriptor: imgFontDescriptor!, size: 24.0)返回与描述符匹配的字体。如果找不到匹配的字体,则会返回默认字体。因此,您无法手动控制字重。它取决于您使用的字体。如果字体支持该字重,则会返回该字重。
还有一件事,您应该使用[UIFontDescriptorFamilyAttribute:“Helvetica”]。这样它将根据族名和字重确定字体名称。
正确的方法是使用Apple库中的常量:
public let UIFontWeightUltraLight: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightThin: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightLight: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightRegular: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightMedium: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightSemibold: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightBold: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightHeavy: CGFloat
@available(iOS 8.2, *)
public let UIFontWeightBlack: CGFloat*/
你应该使用http://iosfonts.com/来确定这个字体系列支持哪种字重。

对于 Helvetica:

let traits = [UIFontWeightTrait: UIFontWeightLight] // UIFontWeightBold / UIFontWeightRegular
let imgFontDescriptor = UIFontDescriptor(fontAttributes: [UIFontDescriptorFamilyAttribute: "Helvetica"])
imgFontDescriptor = imgFontDescriptor.fontDescriptorByAddingAttributes([UIFontDescriptorTraitsAttribute: traits])

啊!我现在明白了。阅读文档时,我被引导相信我可以对字体重量进行细粒度控制或使用提供的常量。我仍然可以指定字体重量的实际数值,但如果字体没有相应的值,它将提供默认值。此外,感谢您提供的网站。这很有帮助! - Tim Sanders

14

使用 Swift 4.1,只需像这样做:

var descriptor = UIFontDescriptor(name: "Helvetica Neue", size: 24.0)
descriptor = descriptor.addingAttributes([UIFontDescriptor.AttributeName.traits : [UIFontDescriptor.TraitKey.weight : UIFont.Weight.light]])
let font = UIFont(descriptor: descriptor, size: 24.0)

10
一个简洁的扩展:
extension UIFont {

  /// Returns a new font with the weight specified.
  /// - Parameter weight: The new font weight
  func withWeight(_ weight: UIFont.Weight) -> UIFont {
    let newDescriptor = fontDescriptor.addingAttributes([.traits: [
      UIFontDescriptor.TraitKey.weight: weight]
    ])
    return UIFont(descriptor: newDescriptor, size: pointSize)
  }
}

6
这是苹果在其CareKit框架中提供的UIFont+Extensions.swift扩展:

https://github.com/carekit-apple/CareKit/

extension UIFont {
    static func preferredCustomFont(forTextStyle textStyle: TextStyle, weight: Weight) -> UIFont {
        let defaultDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle)
        let size = defaultDescriptor.pointSize
        let fontDescriptor = UIFontDescriptor(fontAttributes: [
            UIFontDescriptor.AttributeName.size: size,
            UIFontDescriptor.AttributeName.family: UIFont.systemFont(ofSize: size).familyName
        ])

        // Add the font weight to the descriptor
        let weightedFontDescriptor = fontDescriptor.addingAttributes([
            UIFontDescriptor.AttributeName.traits: [
                UIFontDescriptor.TraitKey.weight: weight
            ]
        ])
        return UIFont(descriptor: weightedFontDescriptor, size: 0)
    }
}

1

基于@pkamb和@Honghao Zhang的答案

您可以仅使用以下扩展更新字重

public extension UIFont {
    /// Returns a new font with the weight specified
    ///
    /// - Parameter weight: The new font weight
    func fontWeight(_ weight: UIFont.Weight) -> UIFont {
        let fontDescriptor = UIFontDescriptor(fontAttributes: [
            UIFontDescriptor.AttributeName.size: pointSize,
            UIFontDescriptor.AttributeName.family: familyName
        ])

        // Add the font weight to the descriptor
        let weightedFontDescriptor = fontDescriptor.addingAttributes([
            UIFontDescriptor.AttributeName.traits: [
                UIFontDescriptor.TraitKey.weight: weight
            ]
        ])
        return UIFont(descriptor: weightedFontDescriptor, size: 0)
    }
}

可以被以下人员使用:

UIFont.body.fontWeight(.light)

这是为了匹配新的SwiftUI API命名而进行的操作。 https://developer.apple.com/documentation/swiftui/text/fontweight(_:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接