UIFont:如何使用 Stylistic Alternate 字符?

UIFont: How to use Stylistic Alternate character?

在我的应用程序中,我想对 'a' 使用 stylistic alternate font 而不是 system font

附上screenshot解释字体的不同渲染。

如何为 UILabelUITextView 启用此行为,以便呈现正确的一层 'a'?

我确实找到了一个 YouTube video link 来解释这一点,但他使用的是 custom font 并且它是硬编码的。我只想使用 system font 但要使用这个备用字符。

我可以用 custom character 硬编码 UILabel,我不确定,因为我想使用 System font。我不想使用 custom Font。可编辑的 UITextView 呢?我们如何让它在用户输入时使用备用 a

这是一个名为 "Alternative Stylistic Sets" 的字体功能,您可以使用 CoreText 配置它。请记住,并非所有字体都有此选项,但系统字体有。但是,您需要弄清楚您想要哪个替代集。

首先,创建您感兴趣的字体:

import CoreText
import UIKit

let baseFont = UIFont.systemFont(ofSize: 72)

然后打印出它的特征:

print(CTFontCopyFeatures(baseFont)!)

找到关于另类风格集的部分,特别是您想要的集,"One storey a:"

    {
    CTFeatureTypeIdentifier = 35;
    CTFeatureTypeName = "Alternative Stylistic Sets";
    CTFeatureTypeSelectors =         (
                    {
            CTFeatureSelectorIdentifier = 2;
            CTFeatureSelectorName = "Straight-sided six and nine";
        },
                    {
            CTFeatureSelectorIdentifier = 4;
            CTFeatureSelectorName = "Open four";
        },
                    {
            CTFeatureSelectorIdentifier = 6;
            CTFeatureSelectorName = "Vertically centered colon";
        },
                    {
            CTFeatureSelectorIdentifier = 10;
            CTFeatureSelectorName = "Vertically compact forms";
        },
                    {
            CTFeatureSelectorIdentifier = 12;
            CTFeatureSelectorName = "High legibility";
        },
                    {
            CTFeatureSelectorIdentifier = 14;
            CTFeatureSelectorName = "One storey a";
        },
        ...

重要的数字是选择器 (CTFeatureSelectorIdentifier),14。有了它,您可以创建新的字体描述符和新字体:

let descriptor = CTFontDescriptorCreateCopyWithFeature(
    baseFont.fontDescriptor,
    kStylisticAlternativesType as CFNumber,
    14 as CFNumber)

或者如果更方便的话,您可以直接在 UIKit 中执行此操作:

let settings: [UIFontDescriptor.FeatureKey: Int] = [
    .featureIdentifier: kStylisticAlternativesType,
    .typeIdentifier: 14
]

let descriptor = baseFont.fontDescriptor.addingAttributes([.featureSettings: [settings]])

(请注意,.featureIdentifier 是 "CTFeatureTypeIdentifier" 而 .typeIdentifier 是 "CTFeatureSelectorIdentifier",这有点令人惊讶。)

然后你可以创建一个新字体(零大小意味着保持大小不变​​):

let font = UIFont(descriptor: descriptor, size: 0)

您可以在任何接受 UIFont 的地方使用它。

这里有一个扩展,可以简化为字体选择替代样式集的过程,假设您知道它的名称:

用法示例:

guard let updatedFont = font.withAlternativeStylisticSet(withName: "Alternate y") else {
    fatalError("Alternative stylistic set is undefined")
}

UIFont 扩展名:

import UIKit
import CoreText

extension UIFont {
    
    /// Returns the font, applying an alternative stylistic style set.
    func withAlternativeStylisticSet(withName name: String) -> UIFont? {
        guard let identifier = alternativeStylisticSetIdentifier(withName: name) else {
            return nil
        }
        
        let settings: [UIFontDescriptor.FeatureKey: Int] = [
            .featureIdentifier: kStylisticAlternativesType,
            .typeIdentifier: identifier
        ]
        
        let fontDescriptor = self.fontDescriptor.addingAttributes([.featureSettings: [settings]])
        return UIFont(descriptor: fontDescriptor, size: 0)
    }
    
    /// Returns the identifier for an alternative stylistic set
    private func alternativeStylisticSetIdentifier(withName selectorName: String) -> Int? {
        guard let ctFeatures = CTFontCopyFeatures(self) else {
            return nil
        }
        
        let features = ctFeatures as [AnyObject] as NSArray
        for feature in features {
            if let featureDict = feature as? [String: Any] {
                if let typeName = featureDict[kCTFontFeatureTypeNameKey as String] as? String {
                    if typeName == "Alternative Stylistic Sets" {
                        if let featureTypeSelectors = featureDict[kCTFontFeatureTypeSelectorsKey as String] as? NSArray {
                            for featureTypeSelector in featureTypeSelectors {
                                if let featureTypeSelectorDict = featureTypeSelector as? [String: Any] {
                                    if let name = featureTypeSelectorDict[kCTFontFeatureSelectorNameKey as String] as? String, let identifier = featureTypeSelectorDict[kCTFontFeatureSelectorIdentifierKey as String] as? Int {
                                        if name == selectorName {
                                            return identifier
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        return nil
    }

}