UIFont:如何使用字形替换字符?

7
在我的应用程序中,我想使用“a”的 字形替代字体 而不是系统字体
附上 截图,说明字体的不同渲染。 enter image description here 如何为 UILabelUITextView 启用这种行为,以便呈现正确的单层'a'?
我确实找到了一个 YouTube 视频 链接,它完全解释了这一点,但他使用了一个 自定义字体 并且是硬编码的。我只想使用 系统字体,但带有这个替代字符。
我可能能够使用 自定义字符 硬编码 UILabel,但我不确定,因为我想使用 系统字体。 我不想使用 自定义字体。 对于可编辑的 UITextView 怎么办?我们如何使它在用户键入时使用备用 a

2
是的,它就在字体面板的排版部分。好处是您可以更改功能并立即在文档中使用以查看其效果。 - matt
@matt !!! 谢谢。我从未见过那个。正是我在寻找的。 - Rob Napier
1
这是MacOS标准字体面板的一部分,前往设置并选择排版。 - FE_Tech
@matt 我正在使用 Sketch,并从中打开字体面板来检查字体设置。然后创建了屏幕截图,以解释是否启用了备选风格集的差异。感谢你的出色工作。我正在阅读你的两本iOS书籍,它们确实帮助了我。 - FE_Tech
1
基本上,使用内置Cocoa字体面板的任何Cocoa应用程序都可能会出现这种情况。我只是使用TextEdit,因为它是每个人都拥有的纯Cocoa应用程序。 - matt
显示剩余4条评论
2个回答

13

这是一种名为“替代风格集”的字体特性,您可以使用CoreText进行配置。请记住,并非所有字体都具有此选项,但系统字体具有此选项。然而,您需要确定您想要哪个替代集。

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

import CoreText
import UIKit

let baseFont = UIFont.systemFont(ofSize: 72)

然后打印出其特征:

print(CTFontCopyFeatures(baseFont)!)

找到“替代风格集”部分,特别是您想要的集合,“一层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 的地方使用它。


有点令人惊讶的事实,我自己称之为一个 bug。 :) - matt
@matt 我一开始也是这么认为的(并且最初称其为答案中的错误),但我不确定。无论如何,我还是提供了反馈(虽然我不指望他们会回复或修复它)。 - Rob Napier
是的,我的已经开放了好几年了。 - matt
@RobNapier 感谢您提供如此深入的完美解释。这确实有效! - FE_Tech

0
这是一个扩展程序,用于简化选择字体的替代样式集,假设您知道其名称:
示例用法:
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
    }

}

1
对于嵌套层数的荒谬,我深表歉意。 - Drew

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